diff --git a/python/build/plugin-list.txt b/python/build/plugin-list.txt index 6c9663172bef..0b27fa9432c9 100644 --- a/python/build/plugin-list.txt +++ b/python/build/plugin-list.txt @@ -1,61 +1,13 @@ -htmltools -JavaScriptLanguage -JavaScriptDebugger -FirefoxConnector -ChromeConnector -python-javascript-debugger cvsIntegration cvs-core cvs-plugin javacvs-src smartcvs-src -PerforceIntegration -Perforce -perforce_util svn4idea git4idea -CSS -css-api -JSInspections -JSIntentionPowerPack -less -sass -haml -haml-plugin hg4idea tasks tasks-core tasks-api github -webDeployment -jdbc-console -django-db-config -database-impl -DatabaseSupport -database-plugin -sql -sql-plugin -coffeescript -remote-run -python-remote-interpreter -vagrant -diagram-api -uml -python-uml -localization -python-localization -rest -python-rest -pycharm-flask -pycharm-numpy -textmate -restClient -puppet -fileWatcher terminal -IntelliLang -IntelliLang-xml -IntelliLang-js -IntelliLang-python -ini4idea -stylus diff --git a/python/build/pycharm_build.gant b/python/build/pycharm_build.gant index 6ca80732d84d..7c17d12ad023 100644 --- a/python/build/pycharm_build.gant +++ b/python/build/pycharm_build.gant @@ -14,6 +14,7 @@ ant.xmlproperty(file: "$home/python/resources/idea/PythonApplicationInfo.xml", c setProperty("system_selector", "PyCharm${p("component.version.major")}0") setProperty("ch", "$home/community") +setProperty("pythonCommunityHome", "$home/python/community") setProperty("dryRun", false) setProperty("jdk16", guessJdk()) @@ -26,8 +27,8 @@ private List pycharmPlatformApiModules() { private List pycharmImplementationModules() { //modules to put into pycharm.jar - return ["yaml", "graph", "coverage-common", "python-community", "python", "python-ide", "python-openapi", "python-psi-api", - "duplicates", "duplicates-xml"] + return ["yaml", "graph", "coverage-common", "python-community", "python", "python-ide", "python-ide-community", + "python-openapi", "python-psi-api", "duplicates", "duplicates-xml"] } class Paths { @@ -137,7 +138,8 @@ target('default': "Build artifacts") { def launcher = "${paths.distWin}/bin/pycharm.exe" List resourcePaths = ["$home/community/community-resources/src", "$home/community/platform/icons/src", - "$home/python/resources"] + "$home/python/resources", + "$pythonCommunityHome/resources"] buildWinLauncher("$home/community", "$home/community/bin/WinLauncher/WinLauncher.exe", launcher, appInfoFile(), "$home/python/build/pycharm_launcher.properties", system_selector, resourcePaths) signExe(launcher) @@ -214,7 +216,7 @@ private scramble() { } private String appInfoFile() { - return "$home/out/pycharm/classes/production/python-ide/idea/PythonApplicationInfo.xml" + return "$home/out/pycharm/classes/production/python-ide-community/idea/PythonApplicationInfo.xml" } private layoutFull(Map args, String target, Set usedJars) { @@ -267,13 +269,15 @@ private layoutFull(Map args, String target, Set usedJars) { fileset(dir: "$home/python/resources") { include(name: "**/tips/**") } + 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") } } - zipfileset(dir: "$home/python/resources/tips", prefix: "tips") } jar("icons.jar") { module("icons") } diff --git a/python/build/pycharm_community_build.gant b/python/build/pycharm_community_build.gant new file mode 100644 index 000000000000..29fd37469040 --- /dev/null +++ b/python/build/pycharm_community_build.gant @@ -0,0 +1,374 @@ +import org.jetbrains.jps.LayoutInfo + +import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome + +includeTargets << new File("${guessHome(this as Script)}/community/build/scripts/utils.gant") +includeTargets << new File("${guessHome(this)}/build/scripts/libLicenses.gant") +includeTargets << new File("${guessHome(this)}/python/build/python_common.gant") + +requireProperty("buildNumber", requireProperty("build.number", snapshot)) + +setProperty("ch", "$home/community") +setProperty("pythonCommunityHome", "$home/python/community") + +// load ApplicationInfo.xml properties +ant.xmlproperty(file: "$pythonCommunityHome/resources/idea/PyCharmCoreApplicationInfo.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("$pythonCommunityHome/build/plugin-list.txt").readLines()) + +private List 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-openapi", "python-psi-api"] +} + +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/pycharmCE" + + distAll = "$sandbox/layout" + distWin = "$sandbox/win" + distMac = "$sandbox/mac" + distUnix = "$sandbox/unix" + artifacts = "$home/out/pycharmCE/artifacts" + + ideaSystem = "$sandbox/system" + ideaConfig = "$sandbox/config" + } +} + +setProperty("paths", new Paths(home)) +setProperty("buildName", "PC-$buildNumber") + +target('default': "Build artifacts") { + def modules = [ + "python-pydev", "colorSchemes", pycharmPlatformApiModules(), pycharmImplementationModules(), pluginFilter + ].flatten() + + 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 = buildModules(modules, ["python/lib"]) + + buildSearchableOptions("${projectBuilder.moduleOutput(findModule("platform-resources"))}/search", [], { + projectBuilder.moduleRuntimeClasspath(findModule("main_pycharm_ce"), false).each { + ant.pathelement(location: it) + } + }, "-Didea.platform.prefix=PyCharmCore -Didea.no.jre.check=true") + + if (!dryRun) { + wireBuildDate(buildName, appInfoFile()) + } + + Map args = [ + buildNumber: buildName, + system_selector: system_selector, + ide_jvm_args: "-Didea.platform.prefix=PyCharmCore -Didea.no.jre.check=true"] + + LayoutInfo layoutInfo = layoutFull(args, paths.distAll, usedJars) + generateLicensesTable("$paths.artifacts/third-party-libraries.txt", layoutInfo.usedModules); + + def egg = buildDebuggerEgg(paths.sandbox, "$home/python", buildName) + ant.copy(file: egg, todir: paths.distAll) + def eggPy3k = buildDebuggerEggPy3K(paths.sandbox, "$home/python", buildName) + ant.copy(file: eggPy3k, todir: paths.distAll) + + layoutWin(args, paths.distWin) + layoutUnix(args, paths.distUnix) + layoutMac(args, paths.distMac) + + ant.echo(message: buildName, file: "${paths.sandbox}/layout/build.txt") + + /* + def launcher = "${paths.distWin}/bin/pycharm.exe" + List resourcePaths = ["$home/community/community-resources/src", + "$home/community/platform/icons/src", + "$home/python/resources", + "$home/python/community/resources"] + buildWinLauncher("$home/community", "$home/community/bin/WinLauncher/WinLauncher.exe", launcher, + appInfoFile(), "$home/python/build/pycharm_launcher.properties", system_selector, resourcePaths) + signExe(launcher) + + buildWinZip("${paths.artifacts}/pycharm${buildName}.zip", [paths.distAll, paths.distWin]) + buildNSIS([paths.distAll, paths.distWin], + "$home/python/build/strings.nsi", "$home/python/build/paths.nsi", + "pycharm", false, true, system_selector) + + String tarRoot = isEap() ? "pycharm-$buildNumber" : "pycharm-${p("component.version.major")}.${p("component.version.minor")}" + buildTarGz(tarRoot, "$paths.artifacts/pycharm${buildName}.tar", [paths.distAll, paths.distUnix]) + + String macAppRoot = isEap() ? "PyCharm ${p("component.version.major")}.${p("component.version.minor")} EAP.app" : "PyCharm.app" + buildMacZip(macAppRoot, "${paths.artifacts}/pycharm${buildName}.sit", [paths.distAll], paths.distMac) + signMacZip("pycharm") + buildDmg("pycharm", "${home}/python/build/DMG_background.png") + */ +} + +private layoutPlugins(layouts) { + dir("plugins") { + /* + layouts.layoutPlugin("django-db-config") + layouts.layoutPlugin("python-javascript-debugger") + layouts.layoutPlugin("remote-run") { + fileset(dir: "$home/plugins/remote-run/lib") { + include(name: "jediterm-0.2.jar") + } + } + layouts.layoutPlugin("python-remote-interpreter") + layouts.layoutPlugin("vagrant") { + fileset(dir: "$home/plugins/vagrant/lib") { + include(name: "jrubyparser-0.2.jar") + } + } + layouts.layoutPlugin("python-uml") + layouts.layoutPlugin("localization") + layouts.layoutPlugin("python-localization") + layouts.layoutPlugin("rest") + layouts.layoutPlugin("python-rest") + layouts.layoutPlugin("pycharm-flask") + layouts.layoutPlugin("pycharm-numpy") + layouts.layoutPlugin("textmate") { + dir("themes") { + fileset(dir: "${home}/plugins/textmate/lib/themes/", includes: "*.tmTheme") + } + fileset(dir: "${home}/plugins/textmate/lib") { + include(name: "jcodings.jar") + include(name: "joni.jar") + } + } + layouts.layoutPlugin("puppet") + layouts.layoutPlugin("fileWatcher") + layouts.layoutPlugin("ini4idea") + */ + } + + layouts.layoutPlugins() +} + +private String appInfoFile() { + return "$home/out/pycharm/classes/production/python-ide-community/idea/PyCharmCoreApplicationInfo.xml" +} + +private layoutFull(Map args, String target, Set usedJars) { + def openapiModules = pycharmPlatformApiModules() + def superLayouts = includeFile("$home/build/scripts/layouts.gant") + 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: "$home/python/resources") { + include(name: "**/tips/**") + } + fileset(dir: "$home/python/community/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("ext") { + fileset(dir: "$home/community/lib") { + include(name: "cglib*.jar") + } + } + + dir("src") { + fileset(dir: "$home/community/lib/src") { + include(name: "trove4j_changes.txt") + include(name: "trove4j_src.jar") + } + + jar("pycharm-pydev-src.zip") { + fileset(dir: "$home/python/pydevSrc") + } + jar("pycharm-openapi-src.zip") { + fileset(dir: "$home/python/openapi/src") + fileset(dir: "$home/python/psi-api/src") + } + } + } + + dir("help") { + fileset(dir: "$home/python/help") { + include(name: "*.pdf") + } + } + + dir("helpers") { + fileset(dir: "$home/python/helpers") + } + + dir("license") { + fileset(dir: "$home/community/license") + } + + layoutPlugins(superLayouts) + + dir("bin") { + fileset(dir: "$home/community/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: "$home/community/bin/win") { + exclude(name: "breakgen*") + } + } + + dir("skeletons") { + fileset(dir: "$home/python/skeletons") { + include(name: "skeletons-win*.zip") + } + } + } + + winScripts(target, "$home/community", "pycharm.bat", args) + winVMOptions(target, args.system_selector, "pycharm.exe") +} + +private layoutUnix(Map args, String target) { + layout(target) { + dir("bin") { + fileset(dir: "$home/community/bin/linux") { + exclude(name: "libbreakgen*") + } + } + } + + ant.copy(file: "$home/python/resources/PyCharm_128.png", tofile: "$target/bin/pycharm.png") + + unixScripts(target, "$home/community", "pycharm.sh", args) + unixVMOptions(target, "pycharm") +} + +private layoutMac(Map _args, String target) { + layout(target) { + dir("bin") { + fileset(dir: "$home/bin") { + include(name: "*.jnilib") + } + } + + dir("skeletons") { + fileset(dir: "$home/python/skeletons") { + include(name: "skeletons-mac*.zip") + } + } + } + + Map args = new HashMap(_args) + args.icns = "$home/python/resources/PyCharm.icns" + args.bundleIdentifier = "com.jetbrains.pycharm" + args.platform_prefix = "Python" + 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, "$home/community", args) +} diff --git a/python/build/python-community-build.iml b/python/build/python-community-build.iml new file mode 100644 index 000000000000..9dcf7cae7b5e --- /dev/null +++ b/python/build/python-community-build.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/python/build/python_plugin_build.gant b/python/build/python_plugin_build.gant index d7bee89e7ca8..6b8aa89a481b 100644 --- a/python/build/python_plugin_build.gant +++ b/python/build/python_plugin_build.gant @@ -333,6 +333,10 @@ target(name: "compile", description: "Compile module python") { patternset(refid: "resources.pt") type(type: "file") } + fileset(dir: "${pluginHome}/community/resources") { + patternset(refid: "resources.pt") + type(type: "file") + } connectorPlugins.each { fileset(dir: "${pluginHome}/${it}/resources") { patternset(refid: "resources.pt") diff --git a/python/ide/python-ide-community.iml b/python/ide/python-ide-community.iml new file mode 100644 index 000000000000..0ec9c8325478 --- /dev/null +++ b/python/ide/python-ide-community.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/python/ide/src/com/jetbrains/python/PlatformPythonModuleType.java b/python/ide/src/com/jetbrains/python/PlatformPythonModuleType.java new file mode 100644 index 000000000000..130e3e0ad974 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/PlatformPythonModuleType.java @@ -0,0 +1,21 @@ +package com.jetbrains.python; + +import com.intellij.ide.util.projectWizard.EmptyModuleBuilder; +import com.intellij.openapi.module.ModuleType; +import org.jetbrains.annotations.NotNull; + +/** + * @author yole + */ +public class PlatformPythonModuleType extends PythonModuleTypeBase { + @NotNull + @Override + public EmptyModuleBuilder createModuleBuilder() { + return new EmptyModuleBuilder() { + @Override + public ModuleType getModuleType() { + return getInstance(); + } + }; + } +} diff --git a/python/ide/src/com/jetbrains/python/PyCharmInitialConfigurator.java b/python/ide/src/com/jetbrains/python/PyCharmInitialConfigurator.java new file mode 100644 index 000000000000..2d18eb67e6dd --- /dev/null +++ b/python/ide/src/com/jetbrains/python/PyCharmInitialConfigurator.java @@ -0,0 +1,79 @@ +package com.jetbrains.python; + +import com.intellij.application.options.InitialConfigurationDialog; +import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.ide.AppLifecycleListener; +import com.intellij.ide.OpenJdkNotifier; +import com.intellij.ide.ui.UISettings; +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; +import com.intellij.openapi.fileTypes.FileTypeManager; +import com.intellij.openapi.wm.WindowManager; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.util.messages.MessageBus; +import com.jetbrains.python.codeInsight.PyCodeInsightSettings; +import org.jetbrains.annotations.NonNls; + +import javax.swing.*; + +/** + * @author yole + */ +@SuppressWarnings({"UtilityClassWithoutPrivateConstructor", "UtilityClassWithPublicConstructor"}) +public class PyCharmInitialConfigurator { + @NonNls private static final String DISPLAYED_PROPERTY = "PyCharm.initialConfigurationShown"; + + public PyCharmInitialConfigurator(MessageBus bus, final PropertiesComponent propertiesComponent, final FileTypeManager fileTypeManager) { + OpenJdkNotifier.checkJdk(bus); + if (!propertiesComponent.getBoolean("PyCharm.InitialConfiguration", false)) { + propertiesComponent.setValue("PyCharm.InitialConfiguration", "true"); + EditorSettingsExternalizable.getInstance().setVirtualSpace(false); + } + if (!propertiesComponent.getBoolean("PyCharm.InitialConfiguration.V2", false)) { + propertiesComponent.setValue("PyCharm.InitialConfiguration.V2", "true"); + 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; + } + if (!propertiesComponent.getBoolean("PyCharm.InitialConfiguration.V3", false)) { + propertiesComponent.setValue("PyCharm.InitialConfiguration.V3", "true"); + UISettings.getInstance().SHOW_MEMORY_INDICATOR = false; + final String ignoredFilesList = fileTypeManager.getIgnoredFilesList(); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + FileTypeManager.getInstance().setIgnoredFilesList(ignoredFilesList + ";*$py.class"); + } + }); + } + if (!propertiesComponent.getBoolean("PyCharm.InitialConfiguration.V4", false)) { + propertiesComponent.setValue("PyCharm.InitialConfiguration.V4", "true"); + PyCodeInsightSettings.getInstance().SHOW_IMPORT_POPUP = false; + } + if (!propertiesComponent.getBoolean("PyCharm.InitialConfiguration.V5", false)) { + propertiesComponent.setValue("PyCharm.InitialConfiguration.V5", "true"); + CodeInsightSettings.getInstance().REFORMAT_ON_PASTE = CodeInsightSettings.NO_REFORMAT; + } + if (!propertiesComponent.isValueSet(DISPLAYED_PROPERTY)) { + bus.connect().subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() { + @Override + public void welcomeScreenDisplayed() { + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + propertiesComponent.setValue(DISPLAYED_PROPERTY, "true"); + showInitialConfigurationDialog(); + } + }); + } + }); + } + } + + private static void showInitialConfigurationDialog() { + final JFrame frame = WindowManager.getInstance().findVisibleFrame(); + new InitialConfigurationDialog(frame, "Python").show(); + } +} diff --git a/python/ide/src/com/jetbrains/python/PyIdeCommonOptionsForm.form b/python/ide/src/com/jetbrains/python/PyIdeCommonOptionsForm.form new file mode 100644 index 000000000000..108b9af1902f --- /dev/null +++ b/python/ide/src/com/jetbrains/python/PyIdeCommonOptionsForm.form @@ -0,0 +1,128 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/ide/src/com/jetbrains/python/PyIdeCommonOptionsForm.java b/python/ide/src/com/jetbrains/python/PyIdeCommonOptionsForm.java new file mode 100644 index 000000000000..0ee44d57b756 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/PyIdeCommonOptionsForm.java @@ -0,0 +1,352 @@ +package com.jetbrains.python; + +import com.intellij.execution.configuration.EnvironmentVariablesComponent; +import com.intellij.execution.util.PathMappingsComponent; +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.SdkModel; +import com.intellij.openapi.projectRoots.impl.SdkListCellRenderer; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel; +import com.intellij.openapi.ui.ComboBox; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.CollectionComboBoxModel; +import com.intellij.ui.HideableDecorator; +import com.intellij.ui.RawCommandLineEditor; +import com.intellij.ui.components.JBCheckBox; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.PathMappingSettings; +import com.jetbrains.python.configuration.PyConfigurableInterpreterList; +import com.jetbrains.python.configuration.PyConfigureInterpretersLinkPanel; +import com.jetbrains.python.run.AbstractPyCommonOptionsForm; +import com.intellij.application.options.ModuleListCellRenderer; +import com.jetbrains.python.run.PyCommonOptionsFormData; +import com.jetbrains.python.sdk.PySdkUtil; +import com.jetbrains.python.sdk.PythonSdkType; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * @author yole + */ +public class PyIdeCommonOptionsForm implements AbstractPyCommonOptionsForm { + private JPanel myMainPanel; + private EnvironmentVariablesComponent myEnvsComponent; + private RawCommandLineEditor myInterpreterOptionsTextField; + private TextFieldWithBrowseButton myWorkingDirectoryTextField; + private JComboBox myInterpreterComboBox; + private JBLabel myPythonInterpreterJBLabel; + private JLabel myProjectLabel; + private JComboBox myModuleCombo; + private JPanel myConfigureInterpretersPanel; + private String mySelectedSdkHome = null; + private PathMappingsComponent myPathMappingsComponent; + private JPanel myHideablePanel; + private JBCheckBox myAddContentRootsCheckbox; + private JBCheckBox myAddSourceRootsCheckbox; + + private JComponent labelAnchor; + private final Project myProject; + private List myPythonSdks; + private boolean myInterpreterRemote; + private final HideableDecorator myDecorator; + + public PyIdeCommonOptionsForm(PyCommonOptionsFormData data) { + myProject = data.getProject(); + myWorkingDirectoryTextField.addBrowseFolderListener("Select Working Directory", "", data.getProject(), + FileChooserDescriptorFactory.createSingleFolderDescriptor()); + + myPythonSdks = new ArrayList(PythonSdkType.getAllSdks()); + myPythonSdks.add(0, null); + + myInterpreterComboBox.setModel(new CollectionComboBoxModel(myPythonSdks, null)); + + final Module[] modules = ModuleManager.getInstance(data.getProject()).getModules(); + if (modules.length == 1) { + setModule(modules[0]); + myProjectLabel.setVisible(false); + myModuleCombo.setVisible(false); + } + else { + final List validModules = data.getValidModules(); + Module selection = validModules.size() > 0 ? validModules.get(0) : null; + myModuleCombo.setModel(new CollectionComboBoxModel(validModules, selection)); + myModuleCombo.setRenderer(new ModuleListCellRenderer()); + myModuleCombo.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + updateDefaultInterpreter((Module)myModuleCombo.getSelectedItem()); + } + }); + updateDefaultInterpreter((Module)myModuleCombo.getSelectedItem()); + } + + setAnchor(myEnvsComponent.getLabel()); + myPathMappingsComponent.setAnchor(myEnvsComponent.getLabel()); + + if (data.showConfigureInterpretersLink()) { + myConfigureInterpretersPanel.add(new PyConfigureInterpretersLinkPanel(myMainPanel)); + } + + addInterpreterComboBoxActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + updateRemoteInterpreterMode(); + } + } + ); + + updateRemoteInterpreterMode(); + + myDecorator = new HideableDecorator(myHideablePanel, "Environment", false) { + @Override + protected void on() { + super.on(); + storeState(); + } + + @Override + protected void off() { + super.off(); + storeState(); + } + private void storeState() { + PropertiesComponent.getInstance().setValue(EXPAND_PROPERTY_KEY, String.valueOf(isExpanded())); + } + }; + myDecorator.setOn(PropertiesComponent.getInstance().getBoolean(EXPAND_PROPERTY_KEY, true)); + myDecorator.setContentComponent(myMainPanel); + } + + @Override + public JComponent getMainPanel() { + return myHideablePanel; + } + + @Override + public void subscribe() { + PyConfigurableInterpreterList myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject); + ProjectSdksModel myProjectSdksModel = myInterpreterList.getModel(); + myProjectSdksModel.addListener(new MyListener(this, myInterpreterList)); + updateSdkList(true, myInterpreterList); + } + + @Override + public JComponent getAnchor() { + return labelAnchor; + } + + @Override + public void setAnchor(JComponent anchor) { + labelAnchor = anchor; + myEnvsComponent.setAnchor(anchor); + myPythonInterpreterJBLabel.setAnchor(anchor); + } + + public String getInterpreterOptions() { + return myInterpreterOptionsTextField.getText().trim(); + } + + public void setInterpreterOptions(String interpreterOptions) { + myInterpreterOptionsTextField.setText(interpreterOptions); + } + + public String getWorkingDirectory() { + return FileUtil.toSystemIndependentName(myWorkingDirectoryTextField.getText().trim()); + } + + public void setWorkingDirectory(String workingDirectory) { + myWorkingDirectoryTextField.setText(workingDirectory == null ? "" : FileUtil.toSystemDependentName(workingDirectory)); + } + + @Override + public String getSdkHome() { + final Sdk selectedSdk = (Sdk)myInterpreterComboBox.getSelectedItem(); + return selectedSdk == null ? null : selectedSdk.getHomePath(); + } + + @Override + public void setSdkHome(String sdkHome) { + mySelectedSdkHome = sdkHome; + } + + @Nullable + @Override + public Module getModule() { + final Module selectedItem = (Module)myModuleCombo.getSelectedItem(); + if (selectedItem != null) { + return selectedItem; + } + final Module[] modules = ModuleManager.getInstance(myProject).getModules(); + if (modules.length == 1) { + return modules[0]; + } + return null; + } + + @Override + public void setModule(Module module) { + myModuleCombo.setSelectedItem(module); + updateDefaultInterpreter(module); + } + + private void updateDefaultInterpreter(Module module) { + final Sdk sdk = module == null ? null : ModuleRootManager.getInstance(module).getSdk(); + String projectSdkName = sdk == null ? "none" : sdk.getName(); + myInterpreterComboBox.setRenderer(new SdkListCellRenderer("Project Default (" + projectSdkName + ")")); + } + + public void updateSdkList(boolean preserveSelection, PyConfigurableInterpreterList myInterpreterList) { + myPythonSdks = myInterpreterList.getAllPythonSdks(); + Sdk selection = preserveSelection ? (Sdk)myInterpreterComboBox.getSelectedItem() : null; + if (!myPythonSdks.contains(selection)) { + selection = null; + } + myPythonSdks.add(0, null); + myInterpreterComboBox.setModel(new CollectionComboBoxModel(myPythonSdks, selection)); + } + + @Override + public boolean isUseModuleSdk() { + return myInterpreterComboBox.getSelectedItem() == null; + } + + @Override + public void setUseModuleSdk(boolean useModuleSdk) { + myInterpreterComboBox.setSelectedItem(useModuleSdk ? null : PythonSdkType.findSdkByPath(myPythonSdks, mySelectedSdkHome)); + } + + public boolean isPassParentEnvs() { + return myEnvsComponent.isPassParentEnvs(); + } + + public void setPassParentEnvs(boolean passParentEnvs) { + myEnvsComponent.setPassParentEnvs(passParentEnvs); + } + + public Map getEnvs() { + return myEnvsComponent.getEnvs(); + } + + public void setEnvs(Map envs) { + myEnvsComponent.setEnvs(envs); + } + + @Override + @Nullable + public PathMappingSettings getMappingSettings() { + if (myInterpreterRemote) { + return myPathMappingsComponent.getMappingSettings(); + } + else { + return null; + } + } + + @Override + public void setMappingSettings(@Nullable PathMappingSettings mappingSettings) { + myPathMappingsComponent.setMappingSettings(mappingSettings); + } + + @Override + public boolean addContentRoots() { + return myAddContentRootsCheckbox.isSelected(); + } + + @Override + public boolean addSourceRoots() { + return myAddSourceRootsCheckbox.isSelected(); + } + + @Override + public void addContentRoots(boolean add) { + myAddContentRootsCheckbox.setSelected(add); + } + + @Override + public void addSourceRoots(boolean add) { + myAddSourceRootsCheckbox.setSelected(add); + } + + private void createUIComponents() { + myInterpreterComboBox = new ComboBox(100); + } + + private void setRemoteInterpreterMode(boolean isInterpreterRemote) { + myInterpreterRemote = isInterpreterRemote; + myPathMappingsComponent.setVisible(isInterpreterRemote); + } + + private void updateRemoteInterpreterMode() { + setRemoteInterpreterMode(PySdkUtil.isRemote(getSdkSelected())); + } + + @Nullable + private Sdk getSdkSelected() { + String sdkHome = getSdkHome(); + if (StringUtil.isEmptyOrSpaces(sdkHome)) { + final Sdk projectJdk = PythonSdkType.findPythonSdk(getModule()); + if (projectJdk != null) { + sdkHome = projectJdk.getHomePath(); + } + } + + return PythonSdkType.findSdkByPath(sdkHome); + } + + @Override + public void addInterpreterComboBoxActionListener(ActionListener listener) { + myInterpreterComboBox.addActionListener(listener); + } + + @Override + public void removeInterpreterComboBoxActionListener(ActionListener listener) { + myInterpreterComboBox.removeActionListener(listener); + } + + private static class MyListener implements SdkModel.Listener { + private final PyIdeCommonOptionsForm myForm; + private PyConfigurableInterpreterList myInterpreterList; + + public MyListener(PyIdeCommonOptionsForm form, PyConfigurableInterpreterList interpreterList) { + myForm = form; + myInterpreterList = interpreterList; + } + + + private void update() { + myForm.updateSdkList(true, myInterpreterList); + } + + @Override + public void sdkAdded(Sdk sdk) { + update(); + } + + @Override + public void beforeSdkRemove(Sdk sdk) { + update(); + } + + @Override + public void sdkChanged(Sdk sdk, String previousName) { + update(); + } + + @Override + public void sdkHomeSelected(Sdk sdk, String newSdkHome) { + } + } +} diff --git a/python/ide/src/com/jetbrains/python/PyIdeCommonOptionsFormFactory.java b/python/ide/src/com/jetbrains/python/PyIdeCommonOptionsFormFactory.java new file mode 100644 index 000000000000..fee2954acc4b --- /dev/null +++ b/python/ide/src/com/jetbrains/python/PyIdeCommonOptionsFormFactory.java @@ -0,0 +1,15 @@ +package com.jetbrains.python; + +import com.jetbrains.python.run.AbstractPyCommonOptionsForm; +import com.jetbrains.python.run.PyCommonOptionsFormData; +import com.jetbrains.python.run.PyCommonOptionsFormFactory; + +/** + * @author yole + */ +public class PyIdeCommonOptionsFormFactory extends PyCommonOptionsFormFactory { + @Override + public PyIdeCommonOptionsForm createForm(PyCommonOptionsFormData data) { + return new PyIdeCommonOptionsForm(data); + } +} diff --git a/python/ide/src/com/jetbrains/python/PyModuleListener.java b/python/ide/src/com/jetbrains/python/PyModuleListener.java new file mode 100644 index 000000000000..22edad8278e0 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/PyModuleListener.java @@ -0,0 +1,36 @@ +package com.jetbrains.python; + +import com.intellij.ProjectTopics; +import com.intellij.execution.RunManagerEx; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.ModuleAdapter; +import com.intellij.openapi.project.Project; +import com.intellij.util.messages.MessageBus; +import com.jetbrains.python.run.AbstractPythonRunConfiguration; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * @author yole + */ +public class PyModuleListener { + public PyModuleListener(MessageBus messageBus) { + messageBus.connect().subscribe(ProjectTopics.MODULES, new ModuleAdapter() { + @Override + public void beforeModuleRemoved(Project project, Module module) { + final RunManagerEx runManager = RunManagerEx.getInstanceEx(project); + final Collection configurations = new ArrayList(runManager.getSortedConfigurations()); + for (RunnerAndConfigurationSettings configuration : configurations) { + if (configuration.getConfiguration() instanceof AbstractPythonRunConfiguration) { + final Module configModule = ((AbstractPythonRunConfiguration)configuration.getConfiguration()).getModule(); + if (configModule == module) { + runManager.removeConfiguration(configuration); + } + } + } + } + }); + } +} diff --git a/python/ide/src/com/jetbrains/python/PyOptionsApplicabilityFilter.java b/python/ide/src/com/jetbrains/python/PyOptionsApplicabilityFilter.java new file mode 100644 index 000000000000..7e27275e8e0e --- /dev/null +++ b/python/ide/src/com/jetbrains/python/PyOptionsApplicabilityFilter.java @@ -0,0 +1,14 @@ +package com.jetbrains.python; + +import com.intellij.application.options.OptionId; +import com.intellij.application.options.OptionsApplicabilityFilter; + +/** + * @author yole + */ +public class PyOptionsApplicabilityFilter extends OptionsApplicabilityFilter { + @Override + public boolean isOptionApplicable(OptionId optionId) { + return optionId == OptionId.RENAME_IN_PLACE; + } +} diff --git a/python/ide/src/com/jetbrains/python/PythonFileTypeFactory.java b/python/ide/src/com/jetbrains/python/PythonFileTypeFactory.java new file mode 100644 index 000000000000..25a2ecc42ad4 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/PythonFileTypeFactory.java @@ -0,0 +1,21 @@ +package com.jetbrains.python; + +import com.intellij.ide.highlighter.XmlFileType; +import com.intellij.openapi.fileTypes.FileTypeConsumer; +import com.intellij.openapi.fileTypes.FileTypeFactory; +import com.jetbrains.pyqt.QtUIFileType; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +/** + * IDE version of file type factory - does not use UltimateVerifier. + * + * @author yole + */ +public class PythonFileTypeFactory extends FileTypeFactory { + public void createFileTypes(@NonNls @NotNull final FileTypeConsumer consumer) { + consumer.consume(PythonFileType.INSTANCE, "py;pyw;"); + consumer.consume(QtUIFileType.INSTANCE, "ui"); + consumer.consume(XmlFileType.INSTANCE, "qrc"); + } +} \ No newline at end of file diff --git a/python/ide/src/com/jetbrains/python/PythonModuleTypeManager.java b/python/ide/src/com/jetbrains/python/PythonModuleTypeManager.java new file mode 100644 index 000000000000..89b682894aae --- /dev/null +++ b/python/ide/src/com/jetbrains/python/PythonModuleTypeManager.java @@ -0,0 +1,14 @@ +package com.jetbrains.python; + +import com.intellij.openapi.module.ModuleType; +import com.intellij.openapi.module.impl.ModuleTypeManagerImpl; + +/** + * @author yole + */ +public class PythonModuleTypeManager extends ModuleTypeManagerImpl { + @Override + public ModuleType getDefaultModuleType() { + return new PlatformPythonModuleType(); + } +} diff --git a/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java b/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java new file mode 100644 index 000000000000..a295070cfb3d --- /dev/null +++ b/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java @@ -0,0 +1,74 @@ +package com.jetbrains.python; + +import com.intellij.openapi.options.ShowSettingsUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.SdkType; +import com.intellij.openapi.projectRoots.impl.SdkListCellRenderer; +import com.intellij.openapi.util.Condition; +import com.intellij.ui.CollectionComboBoxModel; +import com.intellij.ui.ComboboxWithBrowseButton; +import com.intellij.util.containers.ContainerUtil; +import com.jetbrains.python.configuration.PythonSdkConfigurable; +import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; +import com.jetbrains.python.sdk.PythonSdkType; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +/** + * @author yole + */ +public class PythonSdkChooserCombo extends ComboboxWithBrowseButton { + private final List myChangedListeners = ContainerUtil.createLockFreeCopyOnWriteList(); + + //public PythonSdkChooserCombo(final Condition acceptableSdkCondition) { + // this(PythonSdkType.getAllSdks(), acceptableSdkCondition); + //} + + public PythonSdkChooserCombo(final Project project, List sdks, final Condition acceptableSdkCondition) { + Sdk initialSelection = null; + for (Sdk sdk : sdks) { + if (acceptableSdkCondition.value(sdk)) { + initialSelection = sdk; + break; + } + } + getComboBox().setModel(new CollectionComboBoxModel(sdks, initialSelection)); + getComboBox().setRenderer(new SdkListCellRenderer("") { + @Override + protected Icon getSdkIcon(Sdk sdk) { + final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(sdk); + return flavor != null ? flavor.getIcon() : ((SdkType)sdk.getSdkType()).getIcon(); + } + }); + addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + final PythonSdkConfigurable configurable = new PythonSdkConfigurable(project); + configurable.setNewProject(true); + ShowSettingsUtil.getInstance().editConfigurable(PythonSdkChooserCombo.this, configurable); + Sdk selection = configurable.getRealSelectedSdk(); + final List sdks = PythonSdkType.getAllSdks(); + getComboBox().setModel(new CollectionComboBoxModel(sdks, selection)); + notifyChanged(e); + } + }); + getComboBox().addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + notifyChanged(e); + } + }); + } + + private void notifyChanged(ActionEvent e) { + for (ActionListener changedListener : myChangedListeners) { + changedListener.actionPerformed(e); + } + } + + public void addChangedListener(ActionListener listener) { + myChangedListeners.add(listener); + } +} diff --git a/python/ide/src/com/jetbrains/python/PythonSdkConfigurator.java b/python/ide/src/com/jetbrains/python/PythonSdkConfigurator.java new file mode 100644 index 000000000000..8a5397dfb044 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/PythonSdkConfigurator.java @@ -0,0 +1,79 @@ +package com.jetbrains.python; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.SdkAdditionalData; +import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl; +import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.platform.DirectoryProjectConfigurator; +import com.intellij.util.SystemProperties; +import com.jetbrains.python.sdk.PreferredSdkComparator; +import com.jetbrains.python.sdk.PythonSdkAdditionalData; +import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; +import com.jetbrains.python.sdk.PythonSdkType; +import com.jetbrains.python.sdk.flavors.VirtualEnvSdkFlavor; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author dcheryasov + */ +public class PythonSdkConfigurator implements DirectoryProjectConfigurator { + + public void configureProject(final Project project, @NotNull final VirtualFile baseDir, Ref moduleRef) { + // it it a virtualenv? + final PythonSdkType py_sdk_type = PythonSdkType.getInstance(); + //find virtualEnv in project directory + final List candidates = new ArrayList(); + if (project != null) { + final VirtualFile rootDir = project.getBaseDir(); + if (rootDir != null) + candidates.addAll(VirtualEnvSdkFlavor.findInDirectory(rootDir)); + if (!candidates.isEmpty()) { + String filePath = candidates.get(0); + if (StringUtil.startsWithChar(filePath, '~')) { + final String home = SystemProperties.getUserHome(); + filePath = home + filePath.substring(1); + } + final Sdk virtualEnvSdk = SdkConfigurationUtil.createAndAddSDK(filePath, py_sdk_type); + if (virtualEnvSdk != null) { + SdkConfigurationUtil.setDirectoryProjectSdk(project, virtualEnvSdk); + SdkAdditionalData additionalData = virtualEnvSdk.getSdkAdditionalData(); + if (additionalData == null) { + additionalData = new PythonSdkAdditionalData(PythonSdkFlavor.getFlavor(virtualEnvSdk.getHomePath())); + ((ProjectJdkImpl)virtualEnvSdk).setSdkAdditionalData(additionalData); + } + ((PythonSdkAdditionalData) additionalData).associateWithProject(project); + return; + } + return; + } + } + + + final Sdk existing_sdk = ProjectRootManager.getInstance(project).getProjectSdk(); + if (existing_sdk != null && existing_sdk.getSdkType() == py_sdk_type) return; // SdkConfigurationUtil does the same + final File py_exe = PythonSdkType.findExecutableFile(new File(project.getBasePath(), "bin"), "python"); + if (py_exe != null) { + final File env_root = PythonSdkType.getVirtualEnvRoot(py_exe.getPath()); + if (env_root != null) { + // yes, an unknown virtualenv; set it up as SDK + final Sdk an_sdk = SdkConfigurationUtil.createAndAddSDK(py_exe.getPath(), py_sdk_type); + if (an_sdk != null) { + SdkConfigurationUtil.setDirectoryProjectSdk(project, an_sdk); + return; + } + } + } + // default + SdkConfigurationUtil.configureDirectoryProjectSdk(project, new PreferredSdkComparator(), py_sdk_type); + } +} diff --git a/python/ide/src/com/jetbrains/python/PythonSourceRootConfigurator.java b/python/ide/src/com/jetbrains/python/PythonSourceRootConfigurator.java new file mode 100644 index 000000000000..fd6d1615d12a --- /dev/null +++ b/python/ide/src/com/jetbrains/python/PythonSourceRootConfigurator.java @@ -0,0 +1,104 @@ +package com.jetbrains.python; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileEditor.impl.LoadTextUtil; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ContentEntry; +import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.SourceFolder; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.platform.DirectoryProjectConfigurator; +import com.intellij.psi.PsiFileFactory; +import com.jetbrains.python.psi.*; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +/** + * @author yole + */ +public class PythonSourceRootConfigurator implements DirectoryProjectConfigurator { + @NonNls private static final String SETUP_PY = "setup.py"; + + @Override + public void configureProject(Project project, @NotNull final VirtualFile baseDir, Ref moduleRef) { + VirtualFile setupPy = baseDir.findChild(SETUP_PY); + if (setupPy != null) { + final CharSequence content = LoadTextUtil.loadText(setupPy); + PyFile setupPyFile = (PyFile) PsiFileFactory.getInstance(project).createFileFromText(SETUP_PY, content.toString()); + final SetupCallVisitor visitor = new SetupCallVisitor(); + setupPyFile.accept(visitor); + String dir = visitor.getRootPackageDir(); + if (dir != null) { + final VirtualFile rootPackageVFile = baseDir.findFileByRelativePath(dir); + addSourceRoot(project, baseDir, rootPackageVFile, true); + } + } + + VirtualFile web2Py = baseDir.findChild("web2py"); + if (web2Py != null) { + addSourceRoot(project, baseDir, web2Py, false); + } + } + + private static void addSourceRoot(Project project, final VirtualFile baseDir, final VirtualFile root, final boolean unique) { + final Module[] modules = ModuleManager.getInstance(project).getModules(); + if (modules.length > 0 && root != null) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + final ModifiableRootModel model = ModuleRootManager.getInstance(modules[0]).getModifiableModel(); + final ContentEntry[] contentEntries = model.getContentEntries(); + for (ContentEntry contentEntry : contentEntries) { + if (Comparing.equal(contentEntry.getFile(), baseDir)) { + final SourceFolder[] sourceFolders = contentEntry.getSourceFolders(); + if (!unique || sourceFolders.length == 0) { + contentEntry.addSourceFolder(root, false); + } + } + } + model.commit(); + } + }); + } + } + + private static class SetupCallVisitor extends PyRecursiveElementVisitor { + private String myRootPackageDir = null; + + @Override + public void visitPyCallExpression(PyCallExpression node) { + final PyArgumentList argList = node.getArgumentList(); + if (node.isCalleeText("setup") && argList != null) { + final PyKeywordArgument packageDirArg = argList.getKeywordArgument("package_dir"); + if (packageDirArg != null) { + final PyExpression valueExpression = packageDirArg.getValueExpression(); + if (valueExpression instanceof PyDictLiteralExpression) { + PyDictLiteralExpression packageDirDict = (PyDictLiteralExpression)valueExpression; + for (PyKeyValueExpression keyValue : packageDirDict.getElements()) { + final PyExpression keyExpr = keyValue.getKey(); + final PyExpression valueExpr = keyValue.getValue(); + if (keyExpr instanceof PyStringLiteralExpression && valueExpr instanceof PyStringLiteralExpression) { + String key = ((PyStringLiteralExpression) keyExpr).getStringValue(); + String value = ((PyStringLiteralExpression)valueExpr).getStringValue(); + if (key.length() == 0) { + myRootPackageDir = value; + } + } + } + } + } + } + else { + super.visitPyCallExpression(node); + } + } + + public String getRootPackageDir() { + return myRootPackageDir; + } + } +} diff --git a/python/ide/src/com/jetbrains/python/buildout/BuildoutConfigurable.form b/python/ide/src/com/jetbrains/python/buildout/BuildoutConfigurable.form new file mode 100644 index 000000000000..8da1065e40e0 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/buildout/BuildoutConfigurable.form @@ -0,0 +1,35 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/ide/src/com/jetbrains/python/buildout/BuildoutConfigurable.java b/python/ide/src/com/jetbrains/python/buildout/BuildoutConfigurable.java new file mode 100644 index 000000000000..d8f48ee3dfff --- /dev/null +++ b/python/ide/src/com/jetbrains/python/buildout/BuildoutConfigurable.java @@ -0,0 +1,171 @@ +package com.jetbrains.python.buildout; + +import com.intellij.facet.FacetManager; +import com.intellij.facet.ModifiableFacetModel; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.NonDefaultProjectConfigurable; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.util.List; + +/** + * A silly configurable to add buildout facet configurator to PyCharm + * User: dcheryasov + * Date: Jul 28, 2010 4:34:58 PM + */ +public class BuildoutConfigurable implements Configurable, NonDefaultProjectConfigurable { + + private JCheckBox myEnabledCheckbox; + private JPanel myPlaceholder; + private JPanel myMainPanel; + private BuildoutConfigPanel mySettingsPanel; + private final Module myModule; + + public BuildoutConfigurable(@NotNull Module module) { + myModule = module; + BuildoutFacetConfiguration config = null; + BuildoutFacet facet = BuildoutFacet.getInstance(myModule); + if (facet != null) config = facet.getConfiguration(); + if (config == null) config = new BuildoutFacetConfiguration(null); + mySettingsPanel = new BuildoutConfigPanel(myModule, config); + myPlaceholder.add(mySettingsPanel, BorderLayout.CENTER); + final boolean isEnabled = !StringUtil.isEmptyOrSpaces(config.getScriptName()); + myEnabledCheckbox.setSelected(isEnabled); + updateFacetEnabled(isEnabled); + myEnabledCheckbox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + final boolean enabled = myEnabledCheckbox.isSelected(); + updateFacetEnabled(enabled); + } + }); + mySettingsPanel.addFocusListener( + new FocusListener() { + @Override + public void focusGained(FocusEvent focusEvent) { + switchNoticeText(); + } + + @Override + public void focusLost(FocusEvent focusEvent) { } + } + ); + } + + private void updateFacetEnabled(boolean enabled) { + mySettingsPanel.setFacetEnabled(enabled); + UIUtil.setEnabled(mySettingsPanel, enabled, true); + } + + @Nls + @Override + public String getDisplayName() { + return "Buildout Support"; + } + + @Override + public String getHelpTopic() { + return "reference-python-buildout"; + } + + @Override + public JComponent createComponent() { + switchNoticeText(); + return myMainPanel; + } + + @Override + public boolean isModified() { + if (! myEnabledCheckbox.isEnabled()) return false; + final BuildoutFacet facet = myModule != null? BuildoutFacet.getInstance(myModule) : null; + final boolean got_facet = facet != null; + if (myEnabledCheckbox.isSelected() != got_facet) return true; + if (got_facet && myEnabledCheckbox.isSelected()) { + return mySettingsPanel.isModified(facet.getConfiguration()); + } + return false; + } + + @Override + public void apply() throws ConfigurationException { + final BuildoutFacet facet = myModule != null? BuildoutFacet.getInstance(myModule) : null; + final boolean got_facet = facet != null; + boolean facet_is_desired = myEnabledCheckbox.isSelected(); + + mySettingsPanel.apply(); + List paths_from_script = null; + if (facet_is_desired) { + String script_name = mySettingsPanel.getScriptName(); + VirtualFile script_file = BuildoutConfigPanel.getScriptFile(script_name); + paths_from_script = BuildoutFacet.extractBuildoutPaths(script_file); + if (paths_from_script == null) { + throw new ConfigurationException("Failed to extract paths from '" + script_file.getPresentableName() + "'"); + } + mySettingsPanel.getConfiguration().setPaths(paths_from_script); + + } + if (facet_is_desired && ! got_facet) addFacet(mySettingsPanel.getConfiguration()); + if (! facet_is_desired && got_facet) removeFacet(facet); + if (facet_is_desired) BuildoutFacet.attachLibrary(myModule); + else BuildoutFacet.detachLibrary(myModule); + } + + private void addFacet(BuildoutFacetConfiguration config) { + BuildoutFacetConfigurator.setupFacet(myModule, config); + } + + private void removeFacet(BuildoutFacet facet) { + final ModifiableFacetModel model = FacetManager.getInstance(myModule).createModifiableModel(); + model.removeFacet(facet); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + model.commit(); + } + }); + } + + @Override + public void reset() { + // TODO: refactor, see AppEngineConfigurable + if (myModule == null) { + myEnabledCheckbox.setSelected(false); + myEnabledCheckbox.setEnabled(false); + } + else { + myEnabledCheckbox.setEnabled(true); + switchNoticeText(); + final BuildoutFacet instance = BuildoutFacet.getInstance(myModule); + if (instance != null) { + boolean selected = ! StringUtil.isEmptyOrSpaces(instance.getConfiguration().getScriptName()); + myEnabledCheckbox.setSelected(selected); + mySettingsPanel.setEnabled(selected); + mySettingsPanel.reset(); + } + else { + myEnabledCheckbox.setSelected(false); + mySettingsPanel.setEnabled(false); + } + } + } + + private void switchNoticeText() { + mySettingsPanel.showNoticeText(/*DjangoFacet.getInstance(myModule) != null*/ false); + } + + @Override + public void disposeUIResources() { + } + +} diff --git a/python/ide/src/com/jetbrains/python/buildout/BuildoutFacetConfigurator.java b/python/ide/src/com/jetbrains/python/buildout/BuildoutFacetConfigurator.java new file mode 100644 index 000000000000..4eca76e939b0 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/buildout/BuildoutFacetConfigurator.java @@ -0,0 +1,63 @@ +package com.jetbrains.python.buildout; + +import com.intellij.facet.FacetManager; +import com.intellij.facet.ModifiableFacetModel; +import com.intellij.facet.ProjectFacetManager; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.platform.DirectoryProjectConfigurator; +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +/** + * Detects and configures a buildout facet. + * User: dcheryasov + * Date: Jul 26, 2010 6:10:39 PM + */ +public class BuildoutFacetConfigurator implements DirectoryProjectConfigurator { + @Override + public void configureProject(Project project, @NotNull VirtualFile baseDir, Ref moduleRef) { + final Module[] modules = ModuleManager.getInstance(project).getModules(); + if (modules.length > 0) { + final Module module = modules[0]; + if (BuildoutFacet.getInstance(module) == null) { + baseDir.refresh(false, false); + final VirtualFile runner = BuildoutFacet.getRunner(baseDir); + if (runner != null) { + // TODO parse buildout.cfg and find out the part to use for the default script + final File script = BuildoutFacet.findScript(null, "buildout", project.getBaseDir()); + if (script != null) { + final ProjectFacetManager facetManager = ProjectFacetManager.getInstance(module.getProject()); + final BuildoutFacetConfiguration config = facetManager.createDefaultConfiguration(BuildoutFacetType.getInstance()); + config.setScriptName(script.getPath()); + setupFacet(module, config); + } + } + } + } + } + + + static void setupFacet(Module module, @NotNull BuildoutFacetConfiguration config) { + //TODO: refactor, see other python facets + FacetManager facetManager = FacetManager.getInstance(module); + final ModifiableFacetModel model = facetManager.createModifiableModel(); + BuildoutFacetType facetType = BuildoutFacetType.getInstance(); + BuildoutFacet facet = facetManager.createFacet(facetType, facetType.getDefaultFacetName(), config, null); + model.addFacet(facet); + + new WriteAction() { + protected void run(final Result result) throws Throwable { + model.commit(); + } + }.execute(); + facet.updatePaths(); + BuildoutFacet.attachLibrary(module); + } +} diff --git a/python/ide/src/com/jetbrains/python/buildout/BuildoutModulesConfigurable.java b/python/ide/src/com/jetbrains/python/buildout/BuildoutModulesConfigurable.java new file mode 100644 index 000000000000..2e2805eb9745 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/buildout/BuildoutModulesConfigurable.java @@ -0,0 +1,23 @@ +package com.jetbrains.python.buildout; + +import com.intellij.application.options.ModuleAwareProjectConfigurable; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.NonDefaultProjectConfigurable; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +/** + * @author yole + */ +public class BuildoutModulesConfigurable extends ModuleAwareProjectConfigurable implements NonDefaultProjectConfigurable { + public BuildoutModulesConfigurable(Project project) { + super(project, "Buildout Support", "reference-python-buildout"); + } + + @NotNull + @Override + protected Configurable createModuleConfigurable(Module module) { + return new BuildoutConfigurable(module); + } +} diff --git a/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.form b/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.form new file mode 100644 index 000000000000..78c5657ecc11 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.form @@ -0,0 +1,35 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.java b/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.java new file mode 100644 index 000000000000..43bc66f58513 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.java @@ -0,0 +1,35 @@ +package com.jetbrains.python.configuration; + +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.ui.components.JBCheckBox; + +import javax.swing.*; +import java.awt.*; + +/** + * @author yole + */ +public class AddVEnvOptionsDialog extends DialogWrapper { + private JBCheckBox myUseForThisProjectJBCheckBox; + private JBCheckBox myMakeAvailableToAllJBCheckBox; + private JPanel myMainPanel; + + public AddVEnvOptionsDialog(Component parent) { + super(parent, false); + init(); + setTitle("Add Virtualenv"); + } + + @Override + protected JComponent createCenterPanel() { + return myMainPanel; + } + + public boolean useForThisProject() { + return myUseForThisProjectJBCheckBox.isSelected(); + } + + public boolean makeAvailableToAll() { + return myMakeAvailableToAllJBCheckBox.isSelected(); + } +} diff --git a/python/ide/src/com/jetbrains/python/configuration/EditSdkDialog.form b/python/ide/src/com/jetbrains/python/configuration/EditSdkDialog.form new file mode 100644 index 000000000000..a5f3036f09dc --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/EditSdkDialog.form @@ -0,0 +1,69 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/ide/src/com/jetbrains/python/configuration/EditSdkDialog.java b/python/ide/src/com/jetbrains/python/configuration/EditSdkDialog.java new file mode 100644 index 000000000000..8843c2e6635a --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/EditSdkDialog.java @@ -0,0 +1,106 @@ +package com.jetbrains.python.configuration; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.SdkModificator; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.ui.ClickListener; +import com.intellij.ui.DocumentAdapter; +import com.intellij.ui.components.JBCheckBox; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.NullableFunction; +import com.jetbrains.python.sdk.PythonSdkAdditionalData; +import com.jetbrains.python.sdk.PythonSdkType; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import java.awt.event.MouseEvent; + +/** + * @author yole + */ +public class EditSdkDialog extends DialogWrapper { + private JPanel myMainPanel; + private JTextField myNameTextField; + private TextFieldWithBrowseButton myInterpreterPathTextField; + private JBCheckBox myAssociateCheckbox; + private JBLabel myRemoveAssociationLabel; + private final boolean myWasAssociated; + private boolean myAssociationRemoved = false; + + protected EditSdkDialog(Project project, SdkModificator sdk, final NullableFunction nameValidator) { + super(project, true); + setTitle("Edit Python Interpreter"); + myNameTextField.setText(sdk.getName()); + myNameTextField.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + String nameError = nameValidator.fun(getName()); + setErrorText(nameError); + setOKActionEnabled(nameError == null); + } + }); + myInterpreterPathTextField.setText(sdk.getHomePath()); + myInterpreterPathTextField.addBrowseFolderListener("Specify Interpreter Path", null, project, + PythonSdkType.getInstance().getHomeChooserDescriptor()); + myRemoveAssociationLabel.setVisible(false); + if (PythonSdkType.getVirtualEnvRoot(sdk.getHomePath()) == null) { + myAssociateCheckbox.setVisible(false); + } + else { + PythonSdkAdditionalData data = (PythonSdkAdditionalData) sdk.getSdkAdditionalData(); + if (data != null) { + final String path = data.getAssociatedProjectPath(); + if (path != null) { + myAssociateCheckbox.setSelected(true); + final String basePath = project.getBasePath(); + if (basePath != null && !path.equals(FileUtil.toSystemIndependentName(basePath))) { + myAssociateCheckbox.setEnabled(false); + myAssociateCheckbox.setText("Associate this virtual environment with " + FileUtil.toSystemDependentName(path)); + myRemoveAssociationLabel.setVisible(true); + } + } + } + } + myWasAssociated = myAssociateCheckbox.isSelected(); + init(); + new ClickListener() { + @Override + public boolean onClick(MouseEvent e, int clickCount) { + myAssociateCheckbox.setSelected(false); + myAssociateCheckbox.setEnabled(true); + myAssociateCheckbox.setText("Associate this environment with current project"); + myRemoveAssociationLabel.setVisible(false); + myAssociationRemoved = true; + return true; + } + }.installOn(myRemoveAssociationLabel); + } + + @Override + protected JComponent createCenterPanel() { + return myMainPanel; + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myNameTextField; + } + + public String getName() { + return myNameTextField.getText(); + } + + public String getHomePath() { + return myInterpreterPathTextField.getText(); + } + + public boolean associateWithProject() { + return myAssociateCheckbox.isSelected(); + } + + public boolean isAssociateChanged() { + return myWasAssociated != associateWithProject() || myAssociationRemoved; + } +} diff --git a/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.form b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.form new file mode 100644 index 000000000000..79a648b213a2 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.form @@ -0,0 +1,40 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java new file mode 100644 index 000000000000..98364d29bb28 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java @@ -0,0 +1,203 @@ +package com.jetbrains.python.configuration; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.highlighter.EditorHighlighter; +import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.UnnamedConfigurable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.SdkModel; +import com.intellij.openapi.projectRoots.impl.SdkListCellRenderer; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.ModuleRootModificationUtil; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.CollectionComboBoxModel; +import com.jetbrains.python.psi.LanguageLevel; +import com.jetbrains.python.sdk.PySdkListCellRenderer; +import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +/** + * @author yole + */ +public class PyActiveSdkConfigurable implements UnnamedConfigurable { + private JPanel myPanel; + private final Project myProject; + @Nullable private final Module myModule; + private JComboBox mySdkCombo; + private JPanel myConfigureInterpretersPanel; + private PyConfigurableInterpreterList myInterpreterList; + private ProjectSdksModel myProjectSdksModel; + private MyListener myListener; + + public PyActiveSdkConfigurable(Project project) { + myModule = null; + myProject = project; + init(); + } + + public PyActiveSdkConfigurable(@NotNull Module module) { + myModule = module; + myProject = module.getProject(); + init(); + } + + private void init() { + mySdkCombo.setRenderer(new SdkListCellRenderer("")); + + myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject); + myProjectSdksModel = myInterpreterList.getModel(); + myListener = new MyListener(this); + myProjectSdksModel.addListener(myListener); + myConfigureInterpretersPanel.add(new PyConfigureInterpretersLinkPanel(myPanel), BorderLayout.CENTER); + myInterpreterList.setSdkCombo(mySdkCombo); + } + + @Override + public JComponent createComponent() { + return myPanel; + } + + @Override + public boolean isModified() { + final Sdk sdk = getSdk(); + return sdk != myProjectSdksModel.findSdk((Sdk)mySdkCombo.getSelectedItem()); + } + + @Nullable + private Sdk getSdk() { + if (myModule == null) { + return ProjectRootManager.getInstance(myProject).getProjectSdk(); + } + final ModuleRootManager rootManager = ModuleRootManager.getInstance(myModule); + return rootManager.getSdk(); + } + + @Override + public void apply() throws ConfigurationException { + final Sdk selectedSdk = myProjectSdksModel.findSdk((Sdk)mySdkCombo.getSelectedItem()); + if (myModule == null) { + final ProjectRootManager rootManager = ProjectRootManager.getInstance(myProject); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + rootManager.setProjectSdk(selectedSdk); + } + }); + } + else { + ModuleRootModificationUtil.setModuleSdk(myModule, selectedSdk); + } + final Sdk prevSdk = ProjectRootManager.getInstance(myProject).getProjectSdk(); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + ProjectRootManager.getInstance(myProject).setProjectSdk(selectedSdk); + } + }); + + myProjectSdksModel.setProjectSdk(selectedSdk); + + // update string literals if different LanguageLevel was selected + if (prevSdk != null && selectedSdk != null) { + final PythonSdkFlavor flavor1 = PythonSdkFlavor.getFlavor(selectedSdk); + final PythonSdkFlavor flavor2 = PythonSdkFlavor.getFlavor(prevSdk); + if (flavor1 != null && flavor2 != null) { + final LanguageLevel languageLevel1 = flavor1.getLanguageLevel(selectedSdk); + final LanguageLevel languageLevel2 = flavor2.getLanguageLevel(prevSdk); + if ((languageLevel1.isPy3K() && languageLevel2.isPy3K()) || + (!languageLevel1.isPy3K()) && !languageLevel2.isPy3K()) { + return; + } + } + } + rehighlightStrings(myProject); + } + + public static void rehighlightStrings(final @NotNull Project project) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + + for (Editor editor : EditorFactory.getInstance().getAllEditors()) { + if (editor instanceof EditorEx && editor.getProject() == project) { + final VirtualFile vFile = ((EditorEx)editor).getVirtualFile(); + if (vFile != null) { + final EditorHighlighter highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(project, vFile); + ((EditorEx)editor).setHighlighter(highlighter); + } + } + } + } + }); + } + + @Override + public void reset() { + updateSdkList(false); + + final Sdk sdk = getSdk(); + mySdkCombo.setSelectedItem(myProjectSdksModel.getProjectSdks().get(sdk)); + } + + private void updateSdkList(boolean preserveSelection) { + final List sdkList = myInterpreterList.getAllPythonSdks(); + Sdk selection = preserveSelection ? (Sdk)mySdkCombo.getSelectedItem() : null; + if (!sdkList.contains(selection)) { + selection = null; + } + VirtualEnvProjectFilter.removeNotMatching(myProject, sdkList); + // if the selection is a non-matching virtualenv, show it anyway + if (selection != null && !sdkList.contains(selection)) { + sdkList.add(0, selection); + } + sdkList.add(0, null); + mySdkCombo.setRenderer(new PySdkListCellRenderer()); + mySdkCombo.setModel(new CollectionComboBoxModel(sdkList, selection)); + } + + @Override + public void disposeUIResources() { + myProjectSdksModel.removeListener(myListener); + myInterpreterList.disposeModel(); + } + + private static class MyListener implements SdkModel.Listener { + private final PyActiveSdkConfigurable myConfigurable; + + public MyListener(PyActiveSdkConfigurable configurable) { + myConfigurable = configurable; + } + + @Override + public void sdkAdded(Sdk sdk) { + myConfigurable.reset(); + } + + @Override + public void beforeSdkRemove(Sdk sdk) { + myConfigurable.updateSdkList(true); + } + + @Override + public void sdkChanged(Sdk sdk, String previousName) { + myConfigurable.updateSdkList(true); + } + + @Override + public void sdkHomeSelected(Sdk sdk, String newSdkHome) { + } + } +} diff --git a/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkModuleConfigurable.java b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkModuleConfigurable.java new file mode 100644 index 000000000000..2fd855c40551 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkModuleConfigurable.java @@ -0,0 +1,60 @@ +package com.jetbrains.python.configuration; + +import com.intellij.application.options.ModuleAwareProjectConfigurable; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.UnnamedConfigurable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ModuleRootManager; +import com.jetbrains.python.sdk.PythonSdkUpdater; +import com.jetbrains.python.testing.VFSTestFrameworkListener; +import org.jetbrains.annotations.NotNull; + +/** + * @author yole + */ +public class PyActiveSdkModuleConfigurable extends ModuleAwareProjectConfigurable implements Configurable.Composite { + private final Project myProject; + private final Configurable mySdkConfigurable; + + public PyActiveSdkModuleConfigurable(Project project) { + super(project, "Project Interpreter", "reference.settings.project.interpreter"); + myProject = project; + mySdkConfigurable = new PythonSdkConfigurable(project); + } + + @NotNull + @Override + protected UnnamedConfigurable createModuleConfigurable(Module module) { + return new PyActiveSdkConfigurable(module); + } + + @Override + protected UnnamedConfigurable createDefaultProjectConfigurable() { + return new PyActiveSdkConfigurable(myProject); + } + + @Override + public void apply() throws ConfigurationException { + super.apply(); + + // TODO[catherine] proper per-module caching of framework installed state + for (Module module : ModuleManager.getInstance(myProject).getModules()) { + final Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); + if (sdk != null) { + VFSTestFrameworkListener.getInstance().updateAllTestFrameworks(sdk); + break; + } + + } + PythonSdkUpdater.getInstance().updateActiveSdks(myProject, 0); + } + + @Override + public Configurable[] getConfigurables() { + return new Configurable[] { mySdkConfigurable }; + } +} diff --git a/python/ide/src/com/jetbrains/python/configuration/PyConfigurableInterpreterList.java b/python/ide/src/com/jetbrains/python/configuration/PyConfigurableInterpreterList.java new file mode 100644 index 000000000000..b6a080e29072 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/PyConfigurableInterpreterList.java @@ -0,0 +1,68 @@ +package com.jetbrains.python.configuration; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel; +import com.intellij.openapi.util.Comparing; +import com.jetbrains.python.sdk.PythonSdkType; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Manages the SDK model shared between PythonSdkConfigurable and PyActiveSdkConfigurable. + * + * @author yole + */ +public class PyConfigurableInterpreterList { + private ProjectSdksModel myModel; + private JComboBox mySdkCombo; + + public void setSdkCombo(final JComboBox sdkCombo) { + mySdkCombo = sdkCombo; + } + + public void setSelectedSdk(final Sdk selectedSdk) { + if (mySdkCombo != null) + mySdkCombo.setSelectedItem(selectedSdk); + } + + public static PyConfigurableInterpreterList getInstance(Project project) { + return ServiceManager.getService(project, PyConfigurableInterpreterList.class); + } + + public ProjectSdksModel getModel() { + if (myModel == null) { + myModel = new ProjectSdksModel(); + myModel.reset(null); + } + return myModel; + } + + public void disposeModel() { + if (myModel != null) { + myModel.disposeUIResources(); + myModel = null; + } + } + + public List getAllPythonSdks() { + List result = new ArrayList(); + for (Sdk sdk : getModel().getSdks()) { + if (sdk.getSdkType() instanceof PythonSdkType) { + result.add(sdk); + } + } + Collections.sort(result, new Comparator() { + @Override + public int compare(Sdk o1, Sdk o2) { + return Comparing.compare(o1.getName(), o2.getName()); + } + }); + return result; + } +} diff --git a/python/ide/src/com/jetbrains/python/configuration/PyConfigureInterpretersLinkPanel.java b/python/ide/src/com/jetbrains/python/configuration/PyConfigureInterpretersLinkPanel.java new file mode 100644 index 000000000000..0db63b1619be --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/PyConfigureInterpretersLinkPanel.java @@ -0,0 +1,41 @@ +package com.jetbrains.python.configuration; + +import com.intellij.ide.DataManager; +import com.intellij.openapi.options.newEditor.OptionsEditor; +import com.intellij.ui.ClickListener; +import com.intellij.ui.components.JBLabel; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseEvent; + +/** + * @author traff + */ +public class PyConfigureInterpretersLinkPanel extends JPanel { + private final JBLabel myConfigureLabel; + + public PyConfigureInterpretersLinkPanel(final JPanel parentPanel) { + super(new BorderLayout()); + myConfigureLabel = new JBLabel("Configure Interpreters"); + myConfigureLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + new ClickListener() { + @Override + public boolean onClick(MouseEvent e, int clickCount) { + if (clickCount == 1) { + final OptionsEditor optionsEditor = OptionsEditor.KEY.getData(DataManager.getInstance().getDataContext(parentPanel)); + if (optionsEditor != null) { + PythonSdkConfigurable configurable = optionsEditor.findConfigurable(PythonSdkConfigurable.class); + if (configurable != null) { + optionsEditor.clearSearchAndSelect(configurable); + } + return true; + } + } + return false; + } + }.installOn(myConfigureLabel); + + add(myConfigureLabel, BorderLayout.CENTER); + } +} diff --git a/python/ide/src/com/jetbrains/python/configuration/PyDependenciesConfigurable.java b/python/ide/src/com/jetbrains/python/configuration/PyDependenciesConfigurable.java new file mode 100644 index 000000000000..b1269b9f1c5f --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/PyDependenciesConfigurable.java @@ -0,0 +1,32 @@ +package com.jetbrains.python.configuration; + +import com.intellij.application.options.ModuleAwareProjectConfigurable; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.options.OptionalConfigurable; +import com.intellij.openapi.options.UnnamedConfigurable; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +/** + * @author yole + */ +public class PyDependenciesConfigurable extends ModuleAwareProjectConfigurable implements OptionalConfigurable { + private final Project myProject; + + public PyDependenciesConfigurable(Project project) { + super(project, "Project Dependencies", "reference.settingsdialog.project.dependencies"); + myProject = project; + } + + @NotNull + @Override + protected UnnamedConfigurable createModuleConfigurable(Module module) { + return new PyModuleDependenciesConfigurable(module); + } + + @Override + public boolean needDisplay() { + return ModuleManager.getInstance(myProject).getModules().length > 1; + } +} diff --git a/python/ide/src/com/jetbrains/python/configuration/PyDiffPreviewProvider.java b/python/ide/src/com/jetbrains/python/configuration/PyDiffPreviewProvider.java new file mode 100644 index 000000000000..63afbd83e537 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/PyDiffPreviewProvider.java @@ -0,0 +1,59 @@ +package com.jetbrains.python.configuration; + +import com.intellij.openapi.diff.DiffContent; +import com.intellij.openapi.diff.SimpleContent; +import com.intellij.openapi.diff.impl.settings.DiffPreviewProvider; +import com.jetbrains.python.PythonFileType; +import org.jetbrains.annotations.NonNls; + +/** + * @author oleg + */ +public class PyDiffPreviewProvider extends DiffPreviewProvider { + @Override + public DiffContent[] createContents() { + return new DiffContent[]{createContent(LEFT_TEXT), createContent(CENTER_TEXT), createContent(RIGHT_TEXT)}; + } + + private static SimpleContent createContent(final String text) { + return new SimpleContent(text, PythonFileType.INSTANCE); + } + + @NonNls private static final String LEFT_TEXT = "class MyClass\n" + + " value = 123\n" + + "\n" + + " def left_only(self):\n" + + " bar(123)\n" + + "\n" + + " def bar(self, a)\n" + + " print a\n"+ + "\n"+ + " def foo(self):\n" + + " pass # Left changes\n"+ + "\n"+ + "\n"; + @NonNls private static final String CENTER_TEXT = "class MyClass\n" + + " value = 123\n" + + "\n" + + " def bar(self, a)\n" + + " print a\n"+ + "\n"+ + " def foo(self):\n" + + " pass\n" + + "\n" + + "\n" + + " def removed_from_left(self):\n" + + " bar('PyCharmRulezzz')"; + @NonNls private static final String RIGHT_TEXT = "class MyClass\n" + + " value = -123\n" + + "\n" + + " def bar(self, a)\n" + + " print a\n"+ + "\n"+ + " def foo(self):\n" + + " pass # Right changes\n" + + "\n" + + "\n" + + " def removed_from_left(self):\n" + + " bar('PyCharmRulezzz')"; +} diff --git a/python/ide/src/com/jetbrains/python/configuration/PyModuleDependenciesConfigurable.form b/python/ide/src/com/jetbrains/python/configuration/PyModuleDependenciesConfigurable.form new file mode 100644 index 000000000000..d5b0c6bb33c2 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/PyModuleDependenciesConfigurable.form @@ -0,0 +1,29 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/ide/src/com/jetbrains/python/configuration/PyModuleDependenciesConfigurable.java b/python/ide/src/com/jetbrains/python/configuration/PyModuleDependenciesConfigurable.java new file mode 100644 index 000000000000..56045c23266b --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/PyModuleDependenciesConfigurable.java @@ -0,0 +1,118 @@ +package com.jetbrains.python.configuration; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.UnnamedConfigurable; +import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.openapi.roots.ModuleOrderEntry; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.OrderEntry; +import com.intellij.ui.CheckBoxList; +import com.intellij.ui.ToolbarDecorator; +import com.intellij.util.Function; +import com.intellij.util.ui.EditableListModelDecorator; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author yole + */ +public class PyModuleDependenciesConfigurable implements UnnamedConfigurable { + private final Module myModule; + private List myInitialDependencies; + private JPanel myMainPanel; + private JPanel myListHolderPanel; + private CheckBoxList myDependenciesList; + + public PyModuleDependenciesConfigurable(Module module) { + myModule = module; + myDependenciesList = new CheckBoxList(); + resetModel(); + ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myDependenciesList, + new EditableListModelDecorator((DefaultListModel) myDependenciesList.getModel())); + decorator.disableRemoveAction(); + myListHolderPanel.add(decorator.createPanel(), BorderLayout.CENTER); + } + + private void resetModel() { + List possibleDependencies = new ArrayList(); + myInitialDependencies = Arrays.asList(ModuleRootManager.getInstance(myModule).getDependencies()); + possibleDependencies.addAll(myInitialDependencies); + for (Module otherModule : ModuleManager.getInstance(myModule.getProject()).getModules()) { + if (!possibleDependencies.contains(otherModule) && otherModule != myModule) { + possibleDependencies.add(otherModule); + } + } + myDependenciesList.setItems(possibleDependencies, new Function() { + @Override + public String fun(Module module) { + return module.getName(); + } + }); + myDependenciesList.setBorder(null); + for (Module dependency : myInitialDependencies) { + myDependenciesList.setItemSelected(dependency, true); + } + } + + @Nullable + @Override + public JComponent createComponent() { + return myMainPanel; + } + + @Override + public boolean isModified() { + return collectDependencies().equals(myInitialDependencies); + } + + private List collectDependencies() { + List result = new ArrayList(); + for (int i = 0; i < myDependenciesList.getItemsCount(); i++) { + Module module = (Module)myDependenciesList.getItemAt(i); + if (myDependenciesList.isItemSelected(module)) { + result.add(module); + } + } + return result; + } + + @Override + public void apply() throws ConfigurationException { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + ModifiableRootModel model = ModuleRootManager.getInstance(myModule).getModifiableModel(); + List entries = new ArrayList(); + for (OrderEntry entry : model.getOrderEntries()) { + if (entry instanceof ModuleOrderEntry) { + entries.add((ModuleOrderEntry) entry); + } + } + for (ModuleOrderEntry entry : entries) { + model.removeOrderEntry(entry); + } + for (Module module : collectDependencies()) { + model.addModuleOrderEntry(module); + } + model.commit(); + } + }); + } + + @Override + public void reset() { + resetModel(); + } + + @Override + public void disposeUIResources() { + } +} diff --git a/python/ide/src/com/jetbrains/python/configuration/PythonContentEntriesConfigurable.java b/python/ide/src/com/jetbrains/python/configuration/PythonContentEntriesConfigurable.java new file mode 100644 index 000000000000..f7a3e2f03a8a --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/PythonContentEntriesConfigurable.java @@ -0,0 +1,42 @@ +package com.jetbrains.python.configuration; + +import com.intellij.application.options.ModuleAwareProjectConfigurable; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.NonDefaultProjectConfigurable; +import com.intellij.openapi.options.OptionalConfigurable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ui.configuration.PlatformContentEntriesConfigurable; +import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.model.java.JavaSourceRootType; + +/** + * @author yole + */ +public class PythonContentEntriesConfigurable extends ModuleAwareProjectConfigurable implements NonDefaultProjectConfigurable, OptionalConfigurable { + private final Project myProject; + + public PythonContentEntriesConfigurable(final Project project) { + super(project, "Project Structure", "reference.settingsdialog.project.structure"); + myProject = project; + } + + @Nullable + private static Module getSingleModule(Project project) { + return ArrayUtil.getFirstElement(ModuleManager.getInstance(project).getModules()); + } + + @Override + public boolean needDisplay() { + return getSingleModule(myProject) != null; + } + + @NotNull + @Override + protected Configurable createModuleConfigurable(Module module) { + return new PlatformContentEntriesConfigurable(module, JavaSourceRootType.SOURCE); + } +} diff --git a/python/ide/src/com/jetbrains/python/configuration/PythonPathEditor.java b/python/ide/src/com/jetbrains/python/configuration/PythonPathEditor.java new file mode 100644 index 000000000000..97d1c9703138 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/PythonPathEditor.java @@ -0,0 +1,316 @@ +/* + * Copyright 2000-2009 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.configuration; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileTypes.FileTypes; +import com.intellij.openapi.projectRoots.SdkAdditionalData; +import com.intellij.openapi.projectRoots.SdkModificator; +import com.intellij.openapi.projectRoots.ui.SdkPathEditor; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.AnActionButton; +import com.intellij.ui.ListCellRendererWrapper; +import com.intellij.ui.ListUtil; +import com.intellij.ui.ToolbarDecorator; +import com.intellij.ui.components.JBList; +import com.intellij.util.ArrayUtil; +import com.jetbrains.python.sdk.PythonSdkAdditionalData; +import com.jetbrains.python.sdk.PythonSdkType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.List; +import java.util.Set; + +/** + * @author traff + */ +public class PythonPathEditor extends SdkPathEditor { + private final PathListModel myPathListModel; + + public PythonPathEditor(final String displayName, + final OrderRootType orderRootType, + final FileChooserDescriptor descriptor) { + super(displayName, orderRootType, descriptor); + myPathListModel = new PathListModel(orderRootType, getListModel()); + } + + public void reset(@Nullable SdkModificator modificator) { + if (modificator != null) { + List list = Lists.newArrayList(modificator.getRoots(getOrderRootType())); + resetPath(myPathListModel.reset(list, modificator)); + } + else { + setEnabled(false); + } + } + + public void reload(@Nullable SdkModificator sdkModificator) { + if (sdkModificator != null) { + List list = Lists.newArrayList(sdkModificator.getRoots(getOrderRootType())); + resetPath(myPathListModel.reload(list)); + setModified(true); + } + else { + setEnabled(false); + } + } + + @Override + public void apply(SdkModificator sdkModificator) { + sdkModificator.removeRoots(getOrderRootType()); + // add all items + for (int i = 0; i < myPathListModel.getRowCount(); i++) { + VirtualFile path = myPathListModel.getValueAt(i); + if (!myPathListModel.isExcluded(path)) { + sdkModificator.addRoot(path, getOrderRootType()); + } + } + setModified(false); + myPathListModel.apply(sdkModificator); + } + + @Override + protected VirtualFile[] adjustAddedFileSet(Component component, VirtualFile[] files) { + for (int i = 0, filesLength = files.length; i < filesLength; i++) { + if (files[i].getFileType() == FileTypes.ARCHIVE) { + files[i] = JarFileSystem.getInstance().getJarRootForLocalFile(files[i]); + } + } + if (myPathListModel.add(Lists.newArrayList(files))) { + setModified(true); + } + return files; + } + + @Override + protected void doRemoveItems(int[] idxs, JList list) { + List> removed = Lists.newArrayList(); + for (int i : idxs) { + removed.add(Pair.create((VirtualFile)getListModel().get(i), i)); + } + ListUtil.removeIndices(list, myPathListModel.remove(removed)); + list.updateUI(); + setModified(true); + } + + @Override + protected ListCellRenderer createListCellRenderer(JBList list) { + return new PythonPathListCellRenderer(super.createListCellRenderer(list), myPathListModel); + } + + @Override + protected void addToolbarButtons(ToolbarDecorator toolbarDecorator) { + AnActionButton reloadButton = new AnActionButton("Reload List of Paths", AllIcons.Actions.Refresh) { + @Override + public void actionPerformed(AnActionEvent e) { + onReloadButtonClicked(); + } + }; + toolbarDecorator.addExtraAction(reloadButton); + } + + protected void onReloadButtonClicked() { + } + + private static class PathListModel { + private Set myAdded = Sets.newHashSet(); + private Set myExcluded = Sets.newHashSet(); + private Set myFoundFiles = Sets.newHashSet(); + private List myFilteredOut = Lists.newArrayList(); + private final DefaultListModel myListModel; + private final OrderRootType myOrderRootType; + private Set myUserAddedToRemove = Sets.newHashSet(); + + public PathListModel(OrderRootType orderRootType, DefaultListModel listModel) { + myOrderRootType = orderRootType; + myListModel = listModel; + } + + private int getRowCount() { + return myListModel.getSize(); + } + + private VirtualFile getValueAt(int row) { + return (VirtualFile)myListModel.get(row); + } + + public boolean add(List files) { + for (VirtualFile file : files) { + if (!myFoundFiles.contains(file)) { + if (!myExcluded.remove(file)) { //if it was excluded we only delete exclusion mark + myAdded.add(file); + myUserAddedToRemove.remove(file); + } + else { + myFoundFiles.add(file); + return true; + } + } else { + myExcluded.remove(file); + } + } + return false; + } + + public int[] remove(List> files) { + List toRemove = Lists.newArrayList(); + for (Pair e : files) { + if (myAdded.contains(e.first)) { + toRemove.add(e.second); + myAdded.remove(e.first); + myUserAddedToRemove.add(e.first); + } + else if (myExcluded.contains(e.first)) { + myExcluded.remove(e.first); + } + else { + myExcluded.add(e.first); + } + } + return ArrayUtil.toIntArray(toRemove); + } + + public void apply(SdkModificator sdkModificator) { + sdkModificator.setSdkAdditionalData(collectSdkAdditionalData(sdkModificator)); + addFilteredOutRoots(sdkModificator); + } + + private void addFilteredOutRoots(SdkModificator sdkModificator) { + for (VirtualFile file : myFilteredOut) { + sdkModificator.addRoot(file, myOrderRootType); + } + } + + private SdkAdditionalData collectSdkAdditionalData(SdkModificator sdkModificator) { + PythonSdkAdditionalData data = (PythonSdkAdditionalData)sdkModificator.getSdkAdditionalData(); + if (data == null) { + data = new PythonSdkAdditionalData(null); + } + data.setAddedPathsFromVirtualFiles(myAdded); + data.setExcludedPathsFromVirtualFiles(myExcluded); + return data; + } + + public void setAdded(Set added) { + myAdded = Sets.newHashSet(added); + } + + public void setExcluded(Set excluded) { + myExcluded = Sets.newHashSet(excluded); + } + + public String getPresentationSuffix(VirtualFile file) { + if (myAdded.contains(file)) { + return "(added by user)"; + } + if (myExcluded.contains(file)) { + return "(removed by user)"; + } + return ""; + } + + public List reload(List list) { + myFoundFiles.clear(); + myFoundFiles.addAll(list); + List result = filterOutStubs(list, myFilteredOut); + result.removeAll(myUserAddedToRemove); + result.addAll(myAdded); + + return result; + } + + public List reset(List list, SdkModificator modificator) { + myFilteredOut.clear(); + List result = filterOutStubs(list, myFilteredOut); + + myFoundFiles.clear(); + myFoundFiles.addAll(list); + myUserAddedToRemove.clear(); + + if (modificator.getSdkAdditionalData() instanceof PythonSdkAdditionalData) { + PythonSdkAdditionalData data = (PythonSdkAdditionalData)modificator.getSdkAdditionalData(); + setAdded(data.getAddedPathFiles()); + setExcluded(data.getExcludedPathFiles()); + result.addAll(myExcluded); + } + else if (modificator.getSdkAdditionalData() == null) { + myAdded.clear(); + myExcluded.clear(); + } + return result; + } + + private static List filterOutStubs(List list, List filteredOut) { + List result = Lists.newArrayList(); + filteredOut.clear(); + for (VirtualFile file : list) { + if (!isStubPath(file)) { + result.add(file); + } + else { + filteredOut.add(file); + } + } + return result; + } + + private static boolean isStubPath(@NotNull VirtualFile file) { + final String path = PythonSdkType.getSkeletonsRootPath(PathManager.getSystemPath()); + final VirtualFile skeletonRoot = LocalFileSystem.getInstance().findFileByPath(path); + if (skeletonRoot != null) { + return file.getPath().startsWith(skeletonRoot.getPath()); + } + else { + return false; + } + } + + public boolean isExcluded(VirtualFile path) { + return myExcluded.contains(path); + } + } + + + private static class PythonPathListCellRenderer extends ListCellRendererWrapper { + private final PathListModel model; + + public PythonPathListCellRenderer(final ListCellRenderer listCellRenderer, PathListModel model) { + super(); + this.model = model; + } + + @Override + public void customize(JList list, VirtualFile value, int index, boolean selected, boolean hasFocus) { + String suffix = model.getPresentationSuffix(value); + if (suffix.length() > 0) { + suffix = " " + suffix; + } + setText(value != null ? value.getPresentableUrl() + suffix : ""); + } + } +} diff --git a/python/ide/src/com/jetbrains/python/configuration/PythonSdkConfigurable.form b/python/ide/src/com/jetbrains/python/configuration/PythonSdkConfigurable.form new file mode 100644 index 000000000000..b1a81e8c2a8a --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/PythonSdkConfigurable.form @@ -0,0 +1,32 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/ide/src/com/jetbrains/python/configuration/PythonSdkConfigurable.java b/python/ide/src/com/jetbrains/python/configuration/PythonSdkConfigurable.java new file mode 100644 index 000000000000..3365ac8bbf24 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/PythonSdkConfigurable.java @@ -0,0 +1,571 @@ +package com.jetbrains.python.configuration; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.intellij.CommonBundle; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.*; +import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl; +import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.Splitter; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.remotesdk.RemoteCredentials; +import com.intellij.ui.*; +import com.intellij.ui.components.JBList; +import com.intellij.ui.components.JBTabbedPane; +import com.intellij.util.Consumer; +import com.intellij.util.NullableConsumer; +import com.intellij.util.NullableFunction; +import com.intellij.util.containers.FactoryMap; +import com.intellij.webcore.packaging.PackagesNotificationPanel; +import com.jetbrains.python.packaging.ui.PyInstalledPackagesPanel; +import com.jetbrains.python.packaging.ui.PyPackageManagementService; +import com.jetbrains.python.remote.PythonRemoteInterpreterManager; +import com.jetbrains.python.sdk.*; +import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; +import icons.PythonIcons; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.*; +import java.util.*; +import java.util.List; + +public class PythonSdkConfigurable implements Configurable, Configurable.NoScroll { + private JPanel myPanel; + private JList mySdkList; + private JPanel mySplitterHolder; + private PackagesNotificationPanel myNotificationsArea; + private JPanel myNotificationsPlaceholder; + private PythonPathEditor myPathEditor; + private boolean mySdkListChanged = false; + private final PyConfigurableInterpreterList myInterpreterList; + private final ProjectSdksModel myProjectSdksModel; + private final PyInstalledPackagesPanel myPackagesPanel; + + private Map myModificators = new FactoryMap() { + @Override + protected SdkModificator create(Sdk sdk) { + return sdk.getSdkModificator(); + } + }; + private Set myModifiedModificators = new HashSet(); + private Sdk myPreviousSelection; + private boolean myFirstReset; + private final Project myProject; + + private boolean myNewProject = false; + private boolean myShowOtherProjectVirtualenvs = false; + + public void setNewProject(final boolean newProject) { + myNewProject = newProject; + } + + public PythonSdkConfigurable(Project project) { + myProject = project; + myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject); + myProjectSdksModel = myInterpreterList.getModel(); + myFirstReset = true; + + + mySdkList = new JBList(); + mySdkList.setCellRenderer(new PySdkListCellRenderer("", myModificators)); + mySdkList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + ToolbarDecorator decorator = ToolbarDecorator.createDecorator(mySdkList).disableUpDownActions() + .setAddAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton button) { + addSdk(button); + } + }) + .setEditAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton button) { + editSdk(); + } + }) + .setRemoveAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton button) { + removeSdk(); + } + }) + .addExtraAction(new CreateVirtualEnvButton()) + .addExtraAction(new ToggleVirtualEnvFilterButton()); + + + final Splitter splitter = new Splitter(true); + /* + final JScrollPane sdkListPane = ScrollPaneFactory.createScrollPane(mySdkList, + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + sdkListPane.setPreferredSize(new Dimension(10, 10)); + */ + splitter.setFirstComponent(decorator.createPanel()); + + myPathEditor = + new PythonPathEditor("Classes", OrderRootType.CLASSES, FileChooserDescriptorFactory.createAllButJarContentsDescriptor()) { + @Override + protected void onReloadButtonClicked() { + reloadSdk(); + } + }; + + myNotificationsArea = new PackagesNotificationPanel(project); + myNotificationsPlaceholder.add(myNotificationsArea.getComponent(), BorderLayout.CENTER); + + final JBTabbedPane tabbedPane = new JBTabbedPane(SwingConstants.TOP); + myPackagesPanel = new PyInstalledPackagesPanel(project, myNotificationsArea); + tabbedPane.addTab("Packages", myPackagesPanel); + + JPanel panel1 = new JPanel(new GridBagLayout()); + Insets anInsets1 = new Insets(2, 2, 2, 2); + JScrollPane scrollPane1 = ScrollPaneFactory.createScrollPane(myPathEditor.createComponent(), + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane1.setPreferredSize(new Dimension(500, 500)); + panel1.add(scrollPane1, new GridBagConstraints(0, 0, 1, 8, 1.0, 1.0, + GridBagConstraints.CENTER, + GridBagConstraints.BOTH, + anInsets1, 0, 0)); + + tabbedPane.addTab("Paths", panel1); + + splitter.setSecondComponent(tabbedPane); + mySplitterHolder.add(splitter, BorderLayout.CENTER); + + addListeners(); + } + + private void addListeners() { + myProjectSdksModel.addListener(new SdkModel.Listener() { + @Override + public void sdkAdded(Sdk sdk) { + } + + @Override + public void beforeSdkRemove(Sdk sdk) { + } + + @Override + public void sdkChanged(Sdk sdk, String previousName) { + refreshSdkList(); + } + + @Override + public void sdkHomeSelected(Sdk sdk, String newSdkHome) { + } + }); + myPackagesPanel.addPathChangedListener(new Consumer() { + @Override + public void consume(Sdk sdk) { + updateSdkPaths(sdk); + } + }); + + myNotificationsArea.addLinkHandler(PyInstalledPackagesPanel.CREATE_VENV, new Runnable() { + @Override + public void run() { + createVirtualEnv(getSelectedSdk()); + } + }); + mySdkList.addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent event) { + updateUI(getSelectedSdk()); + } + }); + } + + private void updateUI(final Sdk selectedSdk) { + if (myPreviousSelection != null) { + saveSdkPaths(myPreviousSelection); + } + myProjectSdksModel.setProjectSdk(selectedSdk); + updateSdkPaths(selectedSdk); + myPreviousSelection = selectedSdk; + myPackagesPanel.updatePackages(selectedSdk == null ? null : new PyPackageManagementService(myProject, selectedSdk)); + + if (selectedSdk != null) { + myPackagesPanel.updateNotifications(selectedSdk); + } + } + + private void createVirtualEnv(Sdk sdk) { + CreateVirtualEnvDialog.VirtualEnvCallback callback = new CreateVirtualEnvDialog.VirtualEnvCallback() { + @Override + public void virtualEnvCreated(Sdk sdk, boolean associateWithProject, boolean setAsProjectInterpreter) { + PythonSdkType.setupSdkPaths(sdk, myProject, null); + if (associateWithProject) { + SdkAdditionalData additionalData = sdk.getSdkAdditionalData(); + if (additionalData == null) { + additionalData = new PythonSdkAdditionalData(PythonSdkFlavor.getFlavor(sdk.getHomePath())); + ((ProjectJdkImpl)sdk).setSdkAdditionalData(additionalData); + } + if (myNewProject) { + ((PythonSdkAdditionalData)additionalData).associateWithNewProject(); + } + else { + ((PythonSdkAdditionalData)additionalData).associateWithProject(myProject); + } + } + addCreatedSdk(sdk, true, setAsProjectInterpreter); + } + }; + final List allSdks = PyConfigurableInterpreterList.getInstance(myProject).getAllPythonSdks(); + final CreateVirtualEnvDialog dialog = new CreateVirtualEnvDialog(myProject, myNewProject, allSdks, sdk); + dialog.show(); + if (dialog.isOK()) { + dialog.createVirtualEnv(allSdks, callback); + } + } + + public String getDisplayName() { + return "Python Interpreters"; + } + + public String getHelpTopic() { + return "python_interpreter"; + } + + public JComponent createComponent() { + return myPanel; + } + + public boolean isModified() { + return mySdkListChanged || + myProjectSdksModel.isModified() || + myPathEditor.isModified() || + !myModifiedModificators.isEmpty(); + } + + @Nullable + private String getSelectedSdkName() { + final Sdk selectedSdk = (Sdk)mySdkList.getSelectedValue(); + return selectedSdk == null ? null : selectedSdk.getName(); + } + + public void apply() throws ConfigurationException { + if (myPreviousSelection != null) { + saveSdkPaths(myPreviousSelection); + } + for (SdkModificator modificator : myModifiedModificators) { + modificator.commitChanges(); + } + myModificators.clear(); + myModifiedModificators.clear(); + myProjectSdksModel.apply(); + mySdkListChanged = false; + } + + /** + * Returns the stable copy of the SDK currently selected in the SDK table. + * + * @return the selected SDK, or null if there's no selection + */ + @Nullable + public Sdk getRealSelectedSdk() { + return ProjectJdkTable.getInstance().findJdk(getSelectedSdkName()); + } + + @Nullable + public Sdk getSelectedSdk() { + return (Sdk)mySdkList.getSelectedValue(); + } + + public void reset() { + clearModificators(); + if (myFirstReset) { + myFirstReset = false; + } + else { + myProjectSdksModel.reset(null); + } + refreshSdkList(); + final Sdk selectedSdk = getRealSelectedSdk(); + if (selectedSdk != null) { + myPackagesPanel.updateNotifications(selectedSdk); + } + } + + private void clearModificators() { + myModificators.clear(); + myModifiedModificators.clear(); + myPreviousSelection = null; + } + + private void refreshSdkList() { + final List pythonSdks = myInterpreterList.getAllPythonSdks(); + Sdk projectSdk = myProjectSdksModel.getProjectSdk(); + if (!myShowOtherProjectVirtualenvs) { + VirtualEnvProjectFilter.removeNotMatching(myProject, pythonSdks); + } + Collections.sort(pythonSdks, new PreferredSdkComparator()); + mySdkList.setModel(new CollectionListModel(pythonSdks)); + + mySdkListChanged = false; + if (projectSdk == null) projectSdk = getSdk(); + if (projectSdk != null) { + projectSdk = myProjectSdksModel.findSdk(projectSdk.getName()); + mySdkList.clearSelection(); + mySdkList.setSelectedValue(projectSdk, true); + mySdkList.updateUI(); + } + } + + @Nullable + private Sdk getSdk() { + final Module[] modules = ModuleManager.getInstance(myProject).getModules(); + if (modules.length > 0) { + final Module module = modules[0]; + final ModuleRootManager rootManager = ModuleRootManager.getInstance(module); + return rootManager.getSdk(); + } + return ProjectRootManager.getInstance(myProject).getProjectSdk(); + } + + private void addSdk(AnActionButton button) { + InterpreterPathChooser + .show(myProject, myProjectSdksModel.getSdks(), button.getPreferredPopupPoint(), false, new NullableConsumer() { + @Override + public void consume(Sdk sdk) { + addCreatedSdk(sdk, false, false); + } + }); + } + + private void addCreatedSdk(@Nullable final Sdk sdk, boolean newVirtualEnv, boolean makeActive) { + if (sdk != null) { + boolean isVirtualEnv = PythonSdkType.isVirtualEnv(sdk); + boolean askSetAsProjectInterpreter = !myProject.isDefault() && !myNewProject; + if (askSetAsProjectInterpreter && isVirtualEnv && !newVirtualEnv) { + AddVEnvOptionsDialog dialog = new AddVEnvOptionsDialog(myPanel); + dialog.show(); + if (dialog.getExitCode() != DialogWrapper.OK_EXIT_CODE) { + return; + } + SdkModificator modificator = myModificators.get(sdk); + setSdkAssociated(modificator, !dialog.makeAvailableToAll()); + myModifiedModificators.add(modificator); + makeActive = dialog.useForThisProject(); + } + myProjectSdksModel.addSdk(sdk); + refreshSdkList(); + mySdkList.setSelectedValue(sdk, true); + mySdkListChanged = true; + if (askSetAsProjectInterpreter && !isVirtualEnv && !PythonSdkType.isInvalid(sdk) && !PythonSdkType.isIncompleteRemote(sdk)) { + //TODO: make native mac dialog work + makeActive = Messages.showIdeaMessageDialog(myProject, "Do you want to set this interpreter as Project Interpreter?", + "Project Interpreter", + new String[]{CommonBundle.getYesButtonText(), CommonBundle.getNoButtonText()}, 0, null, + null + ) == Messages.YES; + } + if (makeActive) { + SdkConfigurationUtil.setDirectoryProjectSdk(myProject, sdk); + myProjectSdksModel.setProjectSdk(sdk); + myInterpreterList.setSelectedSdk(sdk); + } + } + } + + private void editSdk() { + final Sdk currentSdk = getSelectedSdk(); + if (currentSdk != null) { + if (currentSdk.getSdkAdditionalData() instanceof RemoteCredentials) { + editRemoteSdk(currentSdk); + } + else { + editSdk(currentSdk); + } + updateUI(currentSdk); + } + } + + private void editRemoteSdk(Sdk currentSdk) { + PythonRemoteInterpreterManager remoteInterpreterManager = PythonRemoteInterpreterManager.getInstance(); + if (remoteInterpreterManager != null) { + final SdkModificator modificator = myModificators.get(currentSdk); + Set existingSdks = Sets.newHashSet(myProjectSdksModel.getSdks()); + existingSdks.remove(currentSdk); + if (remoteInterpreterManager.editSdk(myProject, modificator, existingSdks)) { + myModifiedModificators.add(modificator); + } + } + } + + private void editSdk(final Sdk currentSdk) { + final SdkModificator modificator = myModificators.get(currentSdk); + final EditSdkDialog dialog = new EditSdkDialog(myProject, modificator, new NullableFunction() { + @Override + public String fun(String s) { + if (isDuplicateSdkName(s, currentSdk)) { + return "Please specify a unique name for the interpreter"; + } + return null; + } + }); + dialog.show(); + if (dialog.isOK()) { + final boolean pathChanged = !Comparing.equal(currentSdk.getHomePath(), dialog.getHomePath()); + if (!currentSdk.getName().equals(dialog.getName()) || pathChanged || dialog.isAssociateChanged()) { + myModifiedModificators.add(modificator); + modificator.setName(dialog.getName()); + modificator.setHomePath(dialog.getHomePath()); + + if (dialog.isAssociateChanged()) { + setSdkAssociated(modificator, dialog.associateWithProject()); + } + if (pathChanged) { + reloadSdk(currentSdk); + } + } + } + } + + private void setSdkAssociated(SdkModificator modificator, boolean isAssociated) { + PythonSdkAdditionalData additionalData = (PythonSdkAdditionalData)modificator.getSdkAdditionalData(); + if (additionalData == null) { + additionalData = new PythonSdkAdditionalData(PythonSdkFlavor.getFlavor(modificator.getHomePath())); + modificator.setSdkAdditionalData(additionalData); + } + if (isAssociated) { + additionalData.associateWithProject(myProject); + } + else { + additionalData.setAssociatedProjectPath(null); + } + } + + private boolean isDuplicateSdkName(String s, Sdk sdk) { + for (Sdk existingSdk : myProjectSdksModel.getSdks()) { + if (existingSdk == sdk) { + continue; + } + String existingName; + if (myModificators.containsKey(existingSdk)) { + existingName = myModificators.get(existingSdk).getName(); + } + else { + existingName = existingSdk.getName(); + } + if (existingName.equals(s)) { + return true; + } + } + return false; + } + + private void removeSdk() { + final Sdk current_sdk = getSelectedSdk(); + if (current_sdk != null) { + myProjectSdksModel.removeSdk(current_sdk); + if (myModificators.containsKey(current_sdk)) { + SdkModificator modificator = myModificators.get(current_sdk); + myModifiedModificators.remove(modificator); + myModificators.remove(current_sdk); + } + refreshSdkList(); + mySdkListChanged = true; + // TODO select initially selected SDK + if (mySdkList.getSelectedIndex() < 0) { + mySdkList.setSelectedIndex(0); + } + } + } + + private void reloadSdk() { + final Sdk currentSdk = getSelectedSdk(); + if (currentSdk != null) { + myModifiedModificators.add(myModificators.get(currentSdk)); + reloadSdk(currentSdk); + } + } + + private void reloadSdk(Sdk currentSdk) { + PythonSdkType.setupSdkPaths(myProject, null, currentSdk, myModificators.get(currentSdk)); // or must it be a RunWriteAction? + reloadSdkPaths(currentSdk); + } + + public void disposeUIResources() { + myInterpreterList.disposeModel(); + clearModificators(); + myFirstReset = true; + } + + private void saveSdkPaths(Sdk selection) { + SdkModificator modificator = myModificators.get(selection); + if (myPathEditor.isModified()) { + myPathEditor.apply(modificator); + myModifiedModificators.add(modificator); + } + } + + private void reloadSdkPaths(Sdk selection) { + List rootPaths = Lists.newArrayList(); + if (selection != null) { + Collections.addAll(rootPaths, selection.getRootProvider().getFiles(OrderRootType.CLASSES)); + myPathEditor.reload(myModificators.get(selection)); + } + else { + myPathEditor.reload(null); + } + } + + private void updateSdkPaths(final Sdk selection) { + final List rootPaths = Lists.newArrayList(); + if (selection != null) { + Collections.addAll(rootPaths, selection.getRootProvider().getFiles(OrderRootType.CLASSES)); + myPathEditor.reset(myModificators.get(selection)); + } + else { + myPathEditor.reset(null); + } + } + + private class CreateVirtualEnvButton extends AnActionButton implements DumbAware { + public CreateVirtualEnvButton() { + super("Create Virtual Environment", PythonIcons.Python.Virtualenv); + } + + @Override + public void actionPerformed(AnActionEvent e) { + Sdk selectedSdk = getSelectedSdk(); + createVirtualEnv(selectedSdk); + } + } + + private class ToggleVirtualEnvFilterButton extends ToggleActionButton implements DumbAware { + public ToggleVirtualEnvFilterButton() { + super("Show virtual environments associated with other projects", AllIcons.General.Filter); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return myShowOtherProjectVirtualenvs; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + myShowOtherProjectVirtualenvs = state; + refreshSdkList(); + } + } +} diff --git a/python/ide/src/com/jetbrains/python/configuration/VirtualEnvProjectFilter.java b/python/ide/src/com/jetbrains/python/configuration/VirtualEnvProjectFilter.java new file mode 100644 index 000000000000..50b2b7436a3f --- /dev/null +++ b/python/ide/src/com/jetbrains/python/configuration/VirtualEnvProjectFilter.java @@ -0,0 +1,50 @@ +package com.jetbrains.python.configuration; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.io.FileUtil; +import com.jetbrains.python.sdk.PythonSdkAdditionalData; +import com.jetbrains.python.sdk.PythonSdkType; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** +* @author yole +*/ +public class VirtualEnvProjectFilter implements Predicate { + private final String myBasePath; + + public VirtualEnvProjectFilter(@Nullable final String basePath) { + myBasePath = basePath; + } + + @Override + public boolean apply(@Nullable final Sdk input) { + if (input != null && PythonSdkType.isVirtualEnv(input)) { + PythonSdkAdditionalData data = (PythonSdkAdditionalData) input.getSdkAdditionalData(); + if (data != null) { + final String path = data.getAssociatedProjectPath(); + if (path != null && (myBasePath == null || !path.equals(myBasePath))) { + return true; + } + } + } + return false; + } + + public static void removeNotMatching(Project project, List sdks) { + if (project != null) { + final String basePath = project.getBasePath(); + if (basePath != null) { + Iterables.removeIf(sdks, new VirtualEnvProjectFilter(FileUtil.toSystemIndependentName(basePath))); + } + } + } + + public static void removeAllAssociated(List sdks) { + Iterables.removeIf(sdks, new VirtualEnvProjectFilter(null)); + } +} diff --git a/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectAction.java b/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectAction.java new file mode 100644 index 000000000000..1919be622ea3 --- /dev/null +++ b/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectAction.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2009 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.newProject; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.SdkAdditionalData; +import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.platform.DirectoryProjectGenerator; +import com.intellij.platform.NewDirectoryProjectAction; +import com.jetbrains.python.sdk.PythonSdkAdditionalData; +import com.jetbrains.python.sdk.PythonSdkType; + +import java.util.List; + +/** + * User : catherine + */ +public class PythonNewDirectoryProjectAction extends NewDirectoryProjectAction { + private Sdk mySdk; + private boolean myInstallFramework; + + public void actionPerformed(final AnActionEvent e) { + Project project = e.getData(PlatformDataKeys.PROJECT); + if (project == null) { + project = ProjectManager.getInstance().getDefaultProject(); + } + PythonNewDirectoryProjectDialog dlg = new PythonNewDirectoryProjectDialog(project); + dlg.show(); + if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) return; + mySdk = dlg.getSdk(); + myInstallFramework = dlg.installFramework(); + Project newProject = generateProject(project, dlg); + if (newProject != null) { + SdkConfigurationUtil.setDirectoryProjectSdk(newProject, mySdk); + final List sdks = PythonSdkType.getAllSdks(); + for (Sdk sdk : sdks) { + final SdkAdditionalData additionalData = sdk.getSdkAdditionalData(); + if (additionalData instanceof PythonSdkAdditionalData) { + ((PythonSdkAdditionalData) additionalData).reassociateWithCreatedProject(newProject); + } + } + } + } + + @Override + protected Object showSettings(DirectoryProjectGenerator generator, VirtualFile baseDir) + throws ProcessCanceledException { + Object settings = super.showSettings(generator, baseDir); + if (settings instanceof PyNewProjectSettings) { + ((PyNewProjectSettings)settings).setSdk(mySdk); + ((PyNewProjectSettings)settings).setInstallFramework(myInstallFramework); + } + return settings; + } +} diff --git a/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectDialog.java b/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectDialog.java new file mode 100644 index 000000000000..1b959844722e --- /dev/null +++ b/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectDialog.java @@ -0,0 +1,292 @@ +/* + * Copyright 2000-2009 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.newProject; + +import com.intellij.facet.ui.ValidationResult; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.platform.DirectoryProjectGenerator; +import com.intellij.platform.NewDirectoryProjectDialog; +import com.intellij.remotesdk.RemoteSdkData; +import com.intellij.ui.ComboboxWithBrowseButton; +import com.intellij.ui.components.JBCheckBox; +import com.intellij.ui.components.JBLabel; +import com.jetbrains.python.PythonSdkChooserCombo; +import com.jetbrains.python.configuration.VirtualEnvProjectFilter; +import com.jetbrains.python.packaging.PyExternalProcessException; +import com.jetbrains.python.packaging.PyPackage; +import com.jetbrains.python.packaging.PyPackageManager; +import com.jetbrains.python.packaging.PyPackageManagerImpl; +import com.jetbrains.python.remote.PythonRemoteInterpreterManager; +import com.jetbrains.python.remote.RemoteProjectSettings; +import com.jetbrains.python.sdk.PreferredSdkComparator; +import com.jetbrains.python.sdk.PythonSdkType; +import com.jetbrains.python.sdk.flavors.JythonSdkFlavor; +import com.jetbrains.python.sdk.flavors.PyPySdkFlavor; +import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Collections; +import java.util.List; + +/** + * User : catherine + */ +public class PythonNewDirectoryProjectDialog extends NewDirectoryProjectDialog { + private ComboboxWithBrowseButton mySdkCombo; + private final JCheckBox myFrameworkCheckbox; + private boolean myInstallFrameworkChanged; + private final Project myProject; + + protected PythonNewDirectoryProjectDialog(Project project) { + super(project); + myProject = project; + + final List sdks = PythonSdkType.getAllSdks(); + VirtualEnvProjectFilter.removeAllAssociated(sdks); + Collections.sort(sdks, PreferredSdkComparator.INSTANCE); + final Sdk preferred = sdks.isEmpty() ? null : sdks.iterator().next(); + mySdkCombo = new PythonSdkChooserCombo(project, sdks, new Condition() { + @Override + public boolean value(Sdk sdk) { + return sdk == preferred; + } + }); + final JLabel label = new JBLabel("Interpreter:", SwingConstants.LEFT) { + @Override + public Dimension getMinimumSize() { + return new JLabel("Project name:").getPreferredSize(); + } + + @Override + public Dimension getPreferredSize() { + return getMinimumSize(); + } + }; + label.setLabelFor(mySdkCombo); + final JPanel placeholder = getPlaceHolder(); + final GridBagConstraints constraints = new GridBagConstraints(); + constraints.gridx = 0; + constraints.gridy = 0; + constraints.anchor = GridBagConstraints.WEST; + constraints.insets = new Insets(0, 0, 0, 10); + placeholder.add(label, constraints); + + constraints.gridx = 1; + constraints.gridy = 0; + constraints.fill = GridBagConstraints.BOTH; + constraints.weightx = 1.0; + constraints.insets = new Insets(0, 0, 0, 0); + + placeholder.add(mySdkCombo, constraints); + + final ActionListener listener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + checkValid(); + } + }; + + myFrameworkCheckbox = new JBCheckBox("Install "); + constraints.gridx = 0; + constraints.gridy = 1; + constraints.gridwidth = 2; + constraints.weightx = 0.0; + placeholder.add(myFrameworkCheckbox, constraints); + myFrameworkCheckbox.setVisible(false); + + myFrameworkCheckbox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + myInstallFrameworkChanged = true; + checkValid(); + } + }); + + mySdkCombo.addActionListener(listener); + mySdkCombo.getComboBox().addActionListener(listener); + myProjectTypeComboBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + selectCompatiblePython(); + checkValid(); + } + }); + + mySdkCombo.getComboBox().addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent event) { + checkValid(); + } + }); + + final Dimension checkBoxSize = myFrameworkCheckbox.getPreferredSize(); + myRootPane.setPreferredSize(new Dimension(myRootPane.getPreferredSize().width, + myRootPane.getPreferredSize().height + checkBoxSize.height)); + + checkValid(); + } + + @Override + protected Object getEmptyProjectGenerator() { + return new DirectoryProjectGenerator() { + + @NotNull + @Nls + @Override + public String getName() { + return "Empty project"; + } + + @Nullable + @Override + public Object showGenerationSettings(VirtualFile baseDir) throws ProcessCanceledException { + if (PythonSdkType.isRemote(getSdk())) { + PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance(); + assert manager != null; + return manager.showRemoteProjectSettingsDialog(baseDir, (RemoteSdkData)getSdk().getSdkAdditionalData()); + } + else { + return null; + } + } + + @Override + public void generateProject(@NotNull Project project, + @NotNull VirtualFile baseDir, + Object settings, + @NotNull Module module) { + if (settings instanceof RemoteProjectSettings) { + PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance(); + assert manager != null; + manager.createDeployment(project, baseDir, (RemoteProjectSettings)settings, + (RemoteSdkData)getSdk().getSdkAdditionalData()); + } + } + + @NotNull + @Override + public ValidationResult validate(@NotNull String baseDirPath) { + if (PythonSdkType.isRemote(getSdk())) { + if (PythonRemoteInterpreterManager.getInstance() == null) { + return new ValidationResult(PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED); + } + } + return ValidationResult.OK; + } + }; + } + + @Override + public void checkValid() { + super.checkValid(); + Sdk sdk = getSdk(); + if (isOKActionEnabled()) { + setOKActionEnabled(true); + setErrorText(null); + myFrameworkCheckbox.setVisible(false); + + DirectoryProjectGenerator generator = getProjectGenerator(); + final boolean isPy3k = sdk != null && PythonSdkType.getLanguageLevelForSdk(sdk).isPy3K(); + if (sdk != null && PythonSdkType.isRemote(sdk) && !acceptsRemoteSdk(generator)) { + setOKActionEnabled(false); + setErrorText("Please choose a local interpreter"); + } + else if (generator instanceof PyFrameworkProjectGenerator) { + PyFrameworkProjectGenerator frameworkProjectGenerator = (PyFrameworkProjectGenerator)generator; + String frameworkName = frameworkProjectGenerator.getFrameworkTitle(); + if (sdk != null && !isFrameworkInstalled(sdk)) { + final PyPackageManagerImpl packageManager = (PyPackageManagerImpl)PyPackageManager.getInstance(sdk); + final boolean onlyWithCache = + PythonSdkFlavor.getFlavor(sdk) instanceof JythonSdkFlavor || PythonSdkFlavor.getFlavor(sdk) instanceof PyPySdkFlavor; + try { + if (onlyWithCache && packageManager.cacheIsNotNull() || !onlyWithCache) { + final PyPackage pip = packageManager.findPackage("pip"); + myFrameworkCheckbox.setText("Install " + frameworkName); + myFrameworkCheckbox.setMnemonic(frameworkName.charAt(0)); + myFrameworkCheckbox.setVisible(pip != null); + if (!myInstallFrameworkChanged) { + myFrameworkCheckbox.setSelected(pip != null); + } + } + } + catch (PyExternalProcessException e) { + myFrameworkCheckbox.setVisible(false); + } + if (!myFrameworkCheckbox.isSelected()) { + setErrorText("No " + frameworkName + " support installed in selected interpreter"); + setOKActionEnabled(false); + } + } + if (isPy3k && !((PyFrameworkProjectGenerator)generator).supportsPython3()) { + setErrorText(frameworkName + " is not supported for the selected interpreter"); + setOKActionEnabled(false); + } + } + if (sdk == null) { + setOKActionEnabled(false); + setErrorText("No Python interpreter selected"); + } + } + } + + private void selectCompatiblePython() { + DirectoryProjectGenerator generator = getProjectGenerator(); + if (generator instanceof PyFrameworkProjectGenerator && !((PyFrameworkProjectGenerator)generator).supportsPython3()) { + Sdk sdk = getSdk(); + if (sdk != null && PythonSdkType.getLanguageLevelForSdk(sdk).isPy3K()) { + Sdk python2Sdk = PythonSdkType.findPython2Sdk(null); + if (python2Sdk != null) { + mySdkCombo.getComboBox().setSelectedItem(python2Sdk); + mySdkCombo.getComboBox().repaint(); + } + } + } + } + + private boolean isFrameworkInstalled(Sdk sdk) { + PyFrameworkProjectGenerator projectGenerator = (PyFrameworkProjectGenerator)getProjectGenerator(); + + return projectGenerator != null && projectGenerator.isFrameworkInstalled(myProject, sdk); + } + + private static boolean acceptsRemoteSdk(DirectoryProjectGenerator generator) { + if (generator instanceof PyFrameworkProjectGenerator) { + return ((PyFrameworkProjectGenerator)generator).acceptsRemoteSdk(); + } + return true; + } + + public boolean installFramework() { + return myFrameworkCheckbox.isSelected() && myFrameworkCheckbox.isVisible(); + } + + public Sdk getSdk() { + return (Sdk)mySdkCombo.getComboBox().getSelectedItem(); + } +} diff --git a/python/main_pycharm_ce.iml b/python/main_pycharm_ce.iml index b546319d222c..279ba247051e 100644 --- a/python/main_pycharm_ce.iml +++ b/python/main_pycharm_ce.iml @@ -5,7 +5,6 @@ - @@ -14,11 +13,12 @@ - + +
diff --git a/python/pluginSrc/com/jetbrains/python/PythonFileTypeFactory.java b/python/pluginSrc/com/jetbrains/python/PythonFileTypeFactory.java index 2e7c07c2a340..0075ce121406 100644 --- a/python/pluginSrc/com/jetbrains/python/PythonFileTypeFactory.java +++ b/python/pluginSrc/com/jetbrains/python/PythonFileTypeFactory.java @@ -5,7 +5,6 @@ import com.intellij.openapi.fileTypes.FileTypeConsumer; import com.intellij.openapi.fileTypes.FileTypeFactory; import com.intellij.ultimate.PluginVerifier; import com.intellij.ultimate.UltimateVerifier; -import com.jetbrains.cython.CythonFileType; import com.jetbrains.pyqt.QtUIFileType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -22,7 +21,6 @@ public class PythonFileTypeFactory extends FileTypeFactory { public void createFileTypes(@NonNls @NotNull final FileTypeConsumer consumer) { consumer.consume(PythonFileType.INSTANCE, "py;pyw;"); - consumer.consume(CythonFileType.INSTANCE, "pyx;pxd;pxi;"); consumer.consume(QtUIFileType.INSTANCE, "ui"); consumer.consume(XmlFileType.INSTANCE, "qrc"); } diff --git a/python/python-community.iml b/python/python-community.iml index c068a3c62842..676b4956e068 100644 --- a/python/python-community.iml +++ b/python/python-community.iml @@ -4,6 +4,7 @@ + diff --git a/python/python-ide.iml b/python/python-ide.iml index 0a9c7f9fc149..08f4987ff1c8 100644 --- a/python/python-ide.iml +++ b/python/python-ide.iml @@ -5,9 +5,6 @@ - - - @@ -22,6 +19,7 @@ +
diff --git a/python/resources/colorSchemes/PythonDarcula.xml b/python/resources/colorSchemes/PythonDarcula.xml new file mode 100644 index 000000000000..97d59dde6c58 --- /dev/null +++ b/python/resources/colorSchemes/PythonDarcula.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + diff --git a/python/resources/colorSchemes/PythonDefault.xml b/python/resources/colorSchemes/PythonDefault.xml new file mode 100644 index 000000000000..058ac3c402f1 --- /dev/null +++ b/python/resources/colorSchemes/PythonDefault.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + diff --git a/python/resources/fileTemplates/internal/Python Script.py.ft b/python/resources/fileTemplates/internal/Python Script.py.ft new file mode 100644 index 000000000000..edba329d3074 --- /dev/null +++ b/python/resources/fileTemplates/internal/Python Script.py.ft @@ -0,0 +1 @@ +__author__ = '$USER' diff --git a/python/resources/fileTemplates/internal/Python Unit Test.py.ft b/python/resources/fileTemplates/internal/Python Unit Test.py.ft new file mode 100644 index 000000000000..bc5c1af456cb --- /dev/null +++ b/python/resources/fileTemplates/internal/Python Unit Test.py.ft @@ -0,0 +1,10 @@ +__author__ = '$USER' + +import unittest + +class MyTestCase(unittest.TestCase): + def test_something(self): + self.assertEqual(True, False) + +if __name__ == '__main__': + unittest.main() diff --git a/python/resources/fileTemplates/internal/Setup Script.py.ft b/python/resources/fileTemplates/internal/Setup Script.py.ft new file mode 100644 index 000000000000..cb9cb9b36e0b --- /dev/null +++ b/python/resources/fileTemplates/internal/Setup Script.py.ft @@ -0,0 +1,12 @@ +from distutils.core import setup + +setup( + name='$Package_name', + version='$Version', + packages=$PackageList,$PackageDirs + url='$URL', + license='$License', + author='$Author', + author_email='$Author_Email', + description='$Description' +) diff --git a/python/resources/icons/com/jetbrains/pyqt/tsFile.png b/python/resources/icons/com/jetbrains/pyqt/tsFile.png new file mode 100644 index 000000000000..4f962a2bdae2 Binary files /dev/null and b/python/resources/icons/com/jetbrains/pyqt/tsFile.png differ diff --git a/python/resources/icons/com/jetbrains/pyqt/tsFile@2x.png b/python/resources/icons/com/jetbrains/pyqt/tsFile@2x.png new file mode 100644 index 000000000000..628e5af168b1 Binary files /dev/null and b/python/resources/icons/com/jetbrains/pyqt/tsFile@2x.png differ diff --git a/python/resources/icons/com/jetbrains/pyqt/uiForm.png b/python/resources/icons/com/jetbrains/pyqt/uiForm.png new file mode 100644 index 000000000000..048cc19dc1c2 Binary files /dev/null and b/python/resources/icons/com/jetbrains/pyqt/uiForm.png differ diff --git a/python/resources/icons/com/jetbrains/pyqt/uiForm@2x.png b/python/resources/icons/com/jetbrains/pyqt/uiForm@2x.png new file mode 100644 index 000000000000..a4b359a8a4fa Binary files /dev/null and b/python/resources/icons/com/jetbrains/pyqt/uiForm@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/buildout/buildout.png b/python/resources/icons/com/jetbrains/python/buildout/buildout.png new file mode 100644 index 000000000000..d3133dd0b0a8 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/buildout/buildout.png differ diff --git a/python/resources/icons/com/jetbrains/python/buildout/buildout@2x.png b/python/resources/icons/com/jetbrains/python/buildout/buildout@2x.png new file mode 100644 index 000000000000..e0bcb6e65933 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/buildout/buildout@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/debug/commandLine.png b/python/resources/icons/com/jetbrains/python/debug/commandLine.png new file mode 100644 index 000000000000..e064d3f7fec8 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/debug/commandLine.png differ diff --git a/python/resources/icons/com/jetbrains/python/debug/commandLine@2x.png b/python/resources/icons/com/jetbrains/python/debug/commandLine@2x.png new file mode 100644 index 000000000000..a70992337f1e Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/debug/commandLine@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/dotnet.png b/python/resources/icons/com/jetbrains/python/dotnet.png new file mode 100644 index 000000000000..b6c4d3b002ca Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/dotnet.png differ diff --git a/python/resources/icons/com/jetbrains/python/dotnet@2x.png b/python/resources/icons/com/jetbrains/python/dotnet@2x.png new file mode 100644 index 000000000000..1c08889f8a00 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/dotnet@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/jython.png b/python/resources/icons/com/jetbrains/python/jython.png new file mode 100644 index 000000000000..cf8826310828 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/jython.png differ diff --git a/python/resources/icons/com/jetbrains/python/jython@2x.png b/python/resources/icons/com/jetbrains/python/jython@2x.png new file mode 100644 index 000000000000..ff092c1449e6 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/jython@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/nodes/cyan-dot.png b/python/resources/icons/com/jetbrains/python/nodes/cyan-dot.png new file mode 100644 index 000000000000..a60819c781be Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/nodes/cyan-dot.png differ diff --git a/python/resources/icons/com/jetbrains/python/nodes/cyan-dot@2x.png b/python/resources/icons/com/jetbrains/python/nodes/cyan-dot@2x.png new file mode 100644 index 000000000000..6b4f5766e5ce Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/nodes/cyan-dot@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/nodes/lock.png b/python/resources/icons/com/jetbrains/python/nodes/lock.png new file mode 100644 index 000000000000..7ce8afc104ce Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/nodes/lock.png differ diff --git a/python/resources/icons/com/jetbrains/python/nodes/lock@2x.png b/python/resources/icons/com/jetbrains/python/nodes/lock@2x.png new file mode 100644 index 000000000000..84c278689df8 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/nodes/lock@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/nodes/red-inv-triangle.png b/python/resources/icons/com/jetbrains/python/nodes/red-inv-triangle.png new file mode 100644 index 000000000000..0e9ad023fd5e Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/nodes/red-inv-triangle.png differ diff --git a/python/resources/icons/com/jetbrains/python/nodes/red-inv-triangle@2x.png b/python/resources/icons/com/jetbrains/python/nodes/red-inv-triangle@2x.png new file mode 100644 index 000000000000..1c7c45e60332 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/nodes/red-inv-triangle@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/propertyDeleter.png b/python/resources/icons/com/jetbrains/python/propertyDeleter.png new file mode 100644 index 000000000000..98ba3696021d Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/propertyDeleter.png differ diff --git a/python/resources/icons/com/jetbrains/python/propertyDeleter@2x.png b/python/resources/icons/com/jetbrains/python/propertyDeleter@2x.png new file mode 100644 index 000000000000..5e022744fb00 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/propertyDeleter@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/propertyDeleter@2x_dark.png b/python/resources/icons/com/jetbrains/python/propertyDeleter@2x_dark.png new file mode 100644 index 000000000000..fc0705981518 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/propertyDeleter@2x_dark.png differ diff --git a/python/resources/icons/com/jetbrains/python/propertyDeleter_dark.png b/python/resources/icons/com/jetbrains/python/propertyDeleter_dark.png new file mode 100644 index 000000000000..65dd90b3a066 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/propertyDeleter_dark.png differ diff --git a/python/resources/icons/com/jetbrains/python/propertyGetter.png b/python/resources/icons/com/jetbrains/python/propertyGetter.png new file mode 100644 index 000000000000..dd5acf88c19b Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/propertyGetter.png differ diff --git a/python/resources/icons/com/jetbrains/python/propertyGetter@2x.png b/python/resources/icons/com/jetbrains/python/propertyGetter@2x.png new file mode 100644 index 000000000000..bbb02025b6f2 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/propertyGetter@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/propertyGetter@2x_dark.png b/python/resources/icons/com/jetbrains/python/propertyGetter@2x_dark.png new file mode 100644 index 000000000000..69e1fe4d52c6 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/propertyGetter@2x_dark.png differ diff --git a/python/resources/icons/com/jetbrains/python/propertyGetter_dark.png b/python/resources/icons/com/jetbrains/python/propertyGetter_dark.png new file mode 100644 index 000000000000..cf114e38c516 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/propertyGetter_dark.png differ diff --git a/python/resources/icons/com/jetbrains/python/propertySetter.png b/python/resources/icons/com/jetbrains/python/propertySetter.png new file mode 100644 index 000000000000..127501b43681 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/propertySetter.png differ diff --git a/python/resources/icons/com/jetbrains/python/propertySetter@2x.png b/python/resources/icons/com/jetbrains/python/propertySetter@2x.png new file mode 100644 index 000000000000..f9dbee7c1905 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/propertySetter@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/propertySetter@2x_dark.png b/python/resources/icons/com/jetbrains/python/propertySetter@2x_dark.png new file mode 100644 index 000000000000..072bd5e4e8b7 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/propertySetter@2x_dark.png differ diff --git a/python/resources/icons/com/jetbrains/python/propertySetter_dark.png b/python/resources/icons/com/jetbrains/python/propertySetter_dark.png new file mode 100644 index 000000000000..75f0423c8103 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/propertySetter_dark.png differ diff --git a/python/resources/icons/com/jetbrains/python/pypy.png b/python/resources/icons/com/jetbrains/python/pypy.png new file mode 100644 index 000000000000..053d30e46d47 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/pypy.png differ diff --git a/python/resources/icons/com/jetbrains/python/pypy@2x.png b/python/resources/icons/com/jetbrains/python/pypy@2x.png new file mode 100644 index 000000000000..a55a1a20c38d Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/pypy@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/python.png b/python/resources/icons/com/jetbrains/python/python.png new file mode 100644 index 000000000000..02f66b7fabd4 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/python.png differ diff --git a/python/resources/icons/com/jetbrains/python/python@2x.png b/python/resources/icons/com/jetbrains/python/python@2x.png new file mode 100644 index 000000000000..87fff4d6dbfb Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/python@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/pythonClosed.png b/python/resources/icons/com/jetbrains/python/pythonClosed.png new file mode 100644 index 000000000000..c52010aafda5 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/pythonClosed.png differ diff --git a/python/resources/icons/com/jetbrains/python/pythonClosed@2x.png b/python/resources/icons/com/jetbrains/python/pythonClosed@2x.png new file mode 100644 index 000000000000..68d99e2eeb8d Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/pythonClosed@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/pythonTests.png b/python/resources/icons/com/jetbrains/python/pythonTests.png new file mode 100644 index 000000000000..5bcf60420dc9 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/pythonTests.png differ diff --git a/python/resources/icons/com/jetbrains/python/pythonTests@2x.png b/python/resources/icons/com/jetbrains/python/pythonTests@2x.png new file mode 100644 index 000000000000..2bbbec6755ca Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/pythonTests@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/python_24.png b/python/resources/icons/com/jetbrains/python/python_24.png new file mode 100644 index 000000000000..fbdfc6ab02c9 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/python_24.png differ diff --git a/python/resources/icons/com/jetbrains/python/python_24@2x.png b/python/resources/icons/com/jetbrains/python/python_24@2x.png new file mode 100644 index 000000000000..5cef65bcb381 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/python_24@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/virtualenv.png b/python/resources/icons/com/jetbrains/python/virtualenv.png new file mode 100644 index 000000000000..2c0f94f19608 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/virtualenv.png differ diff --git a/python/resources/icons/com/jetbrains/python/virtualenv@2x.png b/python/resources/icons/com/jetbrains/python/virtualenv@2x.png new file mode 100644 index 000000000000..721a63b883bd Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/virtualenv@2x.png differ diff --git a/python/src/idea/PyCharmCoreApplicationInfo.xml b/python/resources/idea/PyCharmCoreApplicationInfo.xml similarity index 100% rename from python/src/idea/PyCharmCoreApplicationInfo.xml rename to python/resources/idea/PyCharmCoreApplicationInfo.xml diff --git a/python/resources/inspectionDescriptions/BuildoutUnresolvedPartInspection.html b/python/resources/inspectionDescriptions/BuildoutUnresolvedPartInspection.html new file mode 100644 index 000000000000..b5b36c654afe --- /dev/null +++ b/python/resources/inspectionDescriptions/BuildoutUnresolvedPartInspection.html @@ -0,0 +1,5 @@ + + +This inspection highlights unresolved buildout parts. + + diff --git a/python/resources/inspectionDescriptions/PyAbstractClassInspection.html b/python/resources/inspectionDescriptions/PyAbstractClassInspection.html new file mode 100644 index 000000000000..41d83f7dcb9c --- /dev/null +++ b/python/resources/inspectionDescriptions/PyAbstractClassInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects when there aren't all abstract properties/methods are defined in subclass + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyArgumentEqualDefaultInspection.html b/python/resources/inspectionDescriptions/PyArgumentEqualDefaultInspection.html new file mode 100644 index 000000000000..16d894ec131c --- /dev/null +++ b/python/resources/inspectionDescriptions/PyArgumentEqualDefaultInspection.html @@ -0,0 +1,7 @@ + + + +This inspection highlights situations, where argument passed to function is equal to default parameter value + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyArgumentListInspection.html b/python/resources/inspectionDescriptions/PyArgumentListInspection.html new file mode 100644 index 000000000000..97d74c0f04ed --- /dev/null +++ b/python/resources/inspectionDescriptions/PyArgumentListInspection.html @@ -0,0 +1,8 @@ + + + +This inspection reports discrepancies between declared parameters and actual arguments, as well as +incorrect arguments (e.g. duplicate named arguments) and incorrect argument order. Decorators are analyzed, too. + + + diff --git a/python/resources/inspectionDescriptions/PyAttributeOutsideInitInspection.html b/python/resources/inspectionDescriptions/PyAttributeOutsideInitInspection.html new file mode 100644 index 000000000000..4e0fee15aac0 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyAttributeOutsideInitInspection.html @@ -0,0 +1,7 @@ + + + +This inspection detects instance attribute definition outside __init__ method + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyAugmentAssignmentInspection.html b/python/resources/inspectionDescriptions/PyAugmentAssignmentInspection.html new file mode 100644 index 000000000000..f9684908d722 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyAugmentAssignmentInspection.html @@ -0,0 +1,7 @@ + + + +This inspection highlights assignment that can be replaced with augmented assignment. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyBroadExceptionInspection.html b/python/resources/inspectionDescriptions/PyBroadExceptionInspection.html new file mode 100644 index 000000000000..67c31a550156 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyBroadExceptionInspection.html @@ -0,0 +1,8 @@ + + + +This inspection highlights too broad exception clauses such as +no exception class specified, or specified as 'Exception'. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyByteLiteralInspection.html b/python/resources/inspectionDescriptions/PyByteLiteralInspection.html new file mode 100644 index 000000000000..e26cb4c5f6ed --- /dev/null +++ b/python/resources/inspectionDescriptions/PyByteLiteralInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects characters > 255 in byte literals. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyCallByClassInspection.html b/python/resources/inspectionDescriptions/PyCallByClassInspection.html new file mode 100644 index 000000000000..7bc9775f3135 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyCallByClassInspection.html @@ -0,0 +1,15 @@ + + +

+This inspection checks for calls of a method by class while passing an instance of a different class +as self parameter:

+
+  foo = Foo()
+  Bar.baz(foo, *more)
+
+

+Sometimes this may be intentional and correct. +But when unintentional, this leads to subtle bugs. +

+ + diff --git a/python/resources/inspectionDescriptions/PyCallingNonCallableInspection.html b/python/resources/inspectionDescriptions/PyCallingNonCallableInspection.html new file mode 100644 index 000000000000..fdc8ed156416 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyCallingNonCallableInspection.html @@ -0,0 +1,7 @@ + + + + This inspection highlights attempts to call objects which are not callable, like, for example, tuples. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyChainedComparisonsInspection.html b/python/resources/inspectionDescriptions/PyChainedComparisonsInspection.html new file mode 100644 index 000000000000..657c918d0c8f --- /dev/null +++ b/python/resources/inspectionDescriptions/PyChainedComparisonsInspection.html @@ -0,0 +1,7 @@ + + + +This inspection highlights chained comparisons that can be simplified. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyClassHasNoInitInspection.html b/python/resources/inspectionDescriptions/PyClassHasNoInitInspection.html new file mode 100644 index 000000000000..c31c8f8bf788 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyClassHasNoInitInspection.html @@ -0,0 +1,7 @@ + + + +This inspection used when a class has no __init__ method, neither its parent classes. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyClassicStyleClassInspection.html b/python/resources/inspectionDescriptions/PyClassicStyleClassInspection.html new file mode 100644 index 000000000000..3894d7fe87c3 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyClassicStyleClassInspection.html @@ -0,0 +1,5 @@ + + +This inspection detects classic style classes usage. + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyComparisonWithNoneInspection.html b/python/resources/inspectionDescriptions/PyComparisonWithNoneInspection.html new file mode 100644 index 000000000000..37a8c54b1627 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyComparisonWithNoneInspection.html @@ -0,0 +1,8 @@ + + + + This inspection highlights comparisons with None. That type of comparisons + should always be done with 'is' or 'is not', never the equality operators. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyCompatibilityInspection.html b/python/resources/inspectionDescriptions/PyCompatibilityInspection.html new file mode 100644 index 000000000000..7bb11b11976d --- /dev/null +++ b/python/resources/inspectionDescriptions/PyCompatibilityInspection.html @@ -0,0 +1,8 @@ + + + +Enable this inspection if you need your code to be compatible with a range of Python versions (for example, if you're building a library). +The range of Python versions with which the code needs to be compatible can be specified in the inspection settings. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyDecoratorInspection.html b/python/resources/inspectionDescriptions/PyDecoratorInspection.html new file mode 100644 index 000000000000..1265e99e5579 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyDecoratorInspection.html @@ -0,0 +1,7 @@ + + +

+This inspection reports usages of @classmethod or @staticmethod decorators on functions outside of a class. +

+ + diff --git a/python/resources/inspectionDescriptions/PyDefaultArgumentInspection.html b/python/resources/inspectionDescriptions/PyDefaultArgumentInspection.html new file mode 100644 index 000000000000..3dced26af04e --- /dev/null +++ b/python/resources/inspectionDescriptions/PyDefaultArgumentInspection.html @@ -0,0 +1,9 @@ + + + + This inspection detects when a mutable value as list or dictionary is detected in a default value for an argument.
+ Default argument values are evaluated only once at function definition time, which means that modifying the + default value of the argument will affect all subsequent calls of the function. +
+ + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyDeprecatedModulesInspection.html b/python/resources/inspectionDescriptions/PyDeprecatedModulesInspection.html new file mode 100644 index 000000000000..0d03609e0a00 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyDeprecatedModulesInspection.html @@ -0,0 +1,7 @@ + + + + This inspection highlight import statements references a module which is deprecated in current Python version. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyDeprecationInspection.html b/python/resources/inspectionDescriptions/PyDeprecationInspection.html new file mode 100644 index 000000000000..46c89d8ed8d1 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyDeprecationInspection.html @@ -0,0 +1,8 @@ + + + + This inspection highlights usages of Python functions, classes or methods which are marked as deprecated (which raise a + DeprecationWarning or a PendingDeprecationWarning). + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyDictCreationInspection.html b/python/resources/inspectionDescriptions/PyDictCreationInspection.html new file mode 100644 index 000000000000..af988be8ce9a --- /dev/null +++ b/python/resources/inspectionDescriptions/PyDictCreationInspection.html @@ -0,0 +1,6 @@ + + +This inspection detects situations when dictionary creation + could be rewritten with dictionary literal. + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyDictDuplicateKeysInspection.html b/python/resources/inspectionDescriptions/PyDictDuplicateKeysInspection.html new file mode 100644 index 000000000000..8f828987ba89 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyDictDuplicateKeysInspection.html @@ -0,0 +1,7 @@ + + + +This inspection highlights using the same value as dictionary key twice. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyDocstringInspection.html b/python/resources/inspectionDescriptions/PyDocstringInspection.html new file mode 100644 index 000000000000..f9f48e5e7204 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyDocstringInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects mismatched parameters in docstring, lack of docstring and an empty docstring. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyDocstringTypesInspection.html b/python/resources/inspectionDescriptions/PyDocstringTypesInspection.html new file mode 100644 index 000000000000..18f462f3a161 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyDocstringTypesInspection.html @@ -0,0 +1,5 @@ + + +This inspection highlights types in docstring which don't match dynamically inferred types. + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyExceptClausesOrderInspection.html b/python/resources/inspectionDescriptions/PyExceptClausesOrderInspection.html new file mode 100644 index 000000000000..d6ade17849ec --- /dev/null +++ b/python/resources/inspectionDescriptions/PyExceptClausesOrderInspection.html @@ -0,0 +1,9 @@ + + + + This inspection highlights situations when except clauses are not in the correct order + (from the more specific to the more generic) or one exception class is caught twice.
+ If you don't fix the order, some exceptions may not be catched by the most specific handler. +
+ + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyExceptionInheritInspection.html b/python/resources/inspectionDescriptions/PyExceptionInheritInspection.html new file mode 100644 index 000000000000..71ba2f796ad0 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyExceptionInheritInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects when a custom exception class is raised but doesn't inherit from the builtin "Exception" class. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyFromFutureImportInspection.html b/python/resources/inspectionDescriptions/PyFromFutureImportInspection.html new file mode 100644 index 000000000000..1161ea2b1035 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyFromFutureImportInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects 'from __future__ import' statements which are used not in the beginning of a file. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyGlobalUndefinedInspection.html b/python/resources/inspectionDescriptions/PyGlobalUndefinedInspection.html new file mode 100644 index 000000000000..f493865fdbd7 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyGlobalUndefinedInspection.html @@ -0,0 +1,7 @@ + + + + This inspection is used when a variable is defined through the "global" statement but the variable is not defined in the module scope. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyInconsistentIndentationInspection.html b/python/resources/inspectionDescriptions/PyInconsistentIndentationInspection.html new file mode 100644 index 000000000000..9fefe1fa6f88 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyInconsistentIndentationInspection.html @@ -0,0 +1,7 @@ + + +This inspection reports inconsistent indentation in Python source files (for example, use of a mixture + of tabs and spaces). + + + diff --git a/python/resources/inspectionDescriptions/PyInitNewSignatureInspection.html b/python/resources/inspectionDescriptions/PyInitNewSignatureInspection.html new file mode 100644 index 000000000000..3c44c678c864 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyInitNewSignatureInspection.html @@ -0,0 +1,7 @@ + + + + This inspection checks mutual compatibility of __new__ and __init__ signatures. + + + diff --git a/python/resources/inspectionDescriptions/PyInterpreterInspection.html b/python/resources/inspectionDescriptions/PyInterpreterInspection.html new file mode 100644 index 000000000000..3d33dd83160b --- /dev/null +++ b/python/resources/inspectionDescriptions/PyInterpreterInspection.html @@ -0,0 +1,5 @@ + + +This inspection notifies you if the current project has no Python interpreter configured or an invalid Python interpreter. + + diff --git a/python/resources/inspectionDescriptions/PyListCreationInspection.html b/python/resources/inspectionDescriptions/PyListCreationInspection.html new file mode 100644 index 000000000000..47dfd11bdbc1 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyListCreationInspection.html @@ -0,0 +1,8 @@ + + + + This inspection detects situations when list creation + could be rewritten with list literal. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyMandatoryEncodingInspection.html b/python/resources/inspectionDescriptions/PyMandatoryEncodingInspection.html new file mode 100644 index 000000000000..8f9c1c87332c --- /dev/null +++ b/python/resources/inspectionDescriptions/PyMandatoryEncodingInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects lack of encoding magic comment for file. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyMethodFirstArgAssignmentInspection.html b/python/resources/inspectionDescriptions/PyMethodFirstArgAssignmentInspection.html new file mode 100644 index 000000000000..37914bb36ac9 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyMethodFirstArgAssignmentInspection.html @@ -0,0 +1,6 @@ + + +This inspection detects cases when first parameter, such as 'self' or 'cls', is reassigned in a method. +In most cases imaginable, there's no point in such reassignment, and it indicates an error. + + diff --git a/python/resources/inspectionDescriptions/PyMethodMayBeStaticInspection.html b/python/resources/inspectionDescriptions/PyMethodMayBeStaticInspection.html new file mode 100644 index 000000000000..b6c42bf6bfb6 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyMethodMayBeStaticInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects any methods which may safely be made static. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyMethodOverridingInspection.html b/python/resources/inspectionDescriptions/PyMethodOverridingInspection.html new file mode 100644 index 000000000000..95b3a9fdaac8 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyMethodOverridingInspection.html @@ -0,0 +1,6 @@ + + + This inspection detects inconsistencies in overriding method signatures. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyMethodParametersInspection.html b/python/resources/inspectionDescriptions/PyMethodParametersInspection.html new file mode 100644 index 000000000000..378ed277a5a9 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyMethodParametersInspection.html @@ -0,0 +1,5 @@ + + +This inspection looks for methods that lack a first parameter (which is usually named self). + + diff --git a/python/resources/inspectionDescriptions/PyMissingConstructorInspection.html b/python/resources/inspectionDescriptions/PyMissingConstructorInspection.html new file mode 100644 index 000000000000..281213efbff0 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyMissingConstructorInspection.html @@ -0,0 +1,7 @@ + + + +This inspection warns if call to super constructor in class is missed + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyNestedDecoratorsInspection.html b/python/resources/inspectionDescriptions/PyNestedDecoratorsInspection.html new file mode 100644 index 000000000000..f42239f79e6e --- /dev/null +++ b/python/resources/inspectionDescriptions/PyNestedDecoratorsInspection.html @@ -0,0 +1,7 @@ + + + +This inspection looks for certain decorators that don't nest well. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyNonAsciiCharInspection.html b/python/resources/inspectionDescriptions/PyNonAsciiCharInspection.html new file mode 100644 index 000000000000..c3f32441a4ac --- /dev/null +++ b/python/resources/inspectionDescriptions/PyNonAsciiCharInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects file contains non-ASCII characters and doesn't have an encoding declaration at the top. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyNoneFunctionAssignmentInspection.html b/python/resources/inspectionDescriptions/PyNoneFunctionAssignmentInspection.html new file mode 100644 index 000000000000..18e712623346 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyNoneFunctionAssignmentInspection.html @@ -0,0 +1,6 @@ + + +This inspection + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyOldStyleClassesInspection.html b/python/resources/inspectionDescriptions/PyOldStyleClassesInspection.html new file mode 100644 index 000000000000..49a70216c631 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyOldStyleClassesInspection.html @@ -0,0 +1,7 @@ + + + + This inspection highlights occurrences of new-style class features in old-style classes. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyPackageRequirementsInspection.html b/python/resources/inspectionDescriptions/PyPackageRequirementsInspection.html new file mode 100644 index 000000000000..ad916132fc56 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyPackageRequirementsInspection.html @@ -0,0 +1,5 @@ + + +This inspection warns about imported or required, but not installed packages. + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyPep8Inspection.html b/python/resources/inspectionDescriptions/PyPep8Inspection.html new file mode 100644 index 000000000000..3fa74ec62d48 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyPep8Inspection.html @@ -0,0 +1,5 @@ + + +This inspection runs the pep8.py tool to check for violations of the PEP 8 coding style guide. + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyPropertyAccessInspection.html b/python/resources/inspectionDescriptions/PyPropertyAccessInspection.html new file mode 100644 index 000000000000..437a05918de1 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyPropertyAccessInspection.html @@ -0,0 +1,8 @@ + + + + This inspection checks that properties are accessed correctly: + read-only not set, write-only not read, non-deletable not deleted. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyPropertyDefinitionInspection.html b/python/resources/inspectionDescriptions/PyPropertyDefinitionInspection.html new file mode 100644 index 000000000000..38b322dd58fe --- /dev/null +++ b/python/resources/inspectionDescriptions/PyPropertyDefinitionInspection.html @@ -0,0 +1,8 @@ + + + + This inspection checks that arguments to property() and functions annotated with + @property and friends look reasonably. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyProtectedMemberInspection.html b/python/resources/inspectionDescriptions/PyProtectedMemberInspection.html new file mode 100644 index 000000000000..d48badd5e7fe --- /dev/null +++ b/python/resources/inspectionDescriptions/PyProtectedMemberInspection.html @@ -0,0 +1,7 @@ + + + +This inspection warns if a protected member is access outside the class or a descendant of the class where it's defined. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyRaisingNewStyleClassInspection.html b/python/resources/inspectionDescriptions/PyRaisingNewStyleClassInspection.html new file mode 100644 index 000000000000..e598afdcce23 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyRaisingNewStyleClassInspection.html @@ -0,0 +1,9 @@ + + + + This inspection detects attempts to raise a new-style class as an exception in projects with + language level less than 2.5. Raising new-style classes is only supported in Python 2.5 + or later. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyRedeclarationInspection.html b/python/resources/inspectionDescriptions/PyRedeclarationInspection.html new file mode 100644 index 000000000000..0175bc40b6a6 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyRedeclarationInspection.html @@ -0,0 +1,10 @@ + + +This inspection detects redeclarations of names without being used in between, like this:
+
def x(): pass
+x = 2
+
+It applies to function and class declarations, and assignments.
+Currently it works in a very limited way and is off by default. +
+ diff --git a/python/resources/inspectionDescriptions/PyRedundantParenthesesInspection.html b/python/resources/inspectionDescriptions/PyRedundantParenthesesInspection.html new file mode 100644 index 000000000000..329591d4e425 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyRedundantParenthesesInspection.html @@ -0,0 +1,7 @@ + + + +This inspection highlights redundant parentheses in statements. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyReturnFromInitInspection.html b/python/resources/inspectionDescriptions/PyReturnFromInitInspection.html new file mode 100644 index 000000000000..7820f74cbede --- /dev/null +++ b/python/resources/inspectionDescriptions/PyReturnFromInitInspection.html @@ -0,0 +1,6 @@ + + +This inspection reports occurrences of return statements with a return value inside + __init__ methods of classes. A constructor should not return any value. + + diff --git a/python/resources/inspectionDescriptions/PySetFunctionToLiteralInspection.html b/python/resources/inspectionDescriptions/PySetFunctionToLiteralInspection.html new file mode 100644 index 000000000000..a54baa15f59f --- /dev/null +++ b/python/resources/inspectionDescriptions/PySetFunctionToLiteralInspection.html @@ -0,0 +1,8 @@ + + + + This inspection detects call for function "set" which can be replaced with + set literal. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyShadowingBuiltinsInspection.html b/python/resources/inspectionDescriptions/PyShadowingBuiltinsInspection.html new file mode 100644 index 000000000000..ed1ea275c416 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyShadowingBuiltinsInspection.html @@ -0,0 +1,5 @@ + + +This inspection detects shadowing of built-in names such as "len" or "list". + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PySimplifyBooleanCheckInspection.html b/python/resources/inspectionDescriptions/PySimplifyBooleanCheckInspection.html new file mode 100644 index 000000000000..dc5059e90a35 --- /dev/null +++ b/python/resources/inspectionDescriptions/PySimplifyBooleanCheckInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects equality comparison with a boolean literal. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PySingleQuotedDocstringInspection.html b/python/resources/inspectionDescriptions/PySingleQuotedDocstringInspection.html new file mode 100644 index 000000000000..e1674d5aa517 --- /dev/null +++ b/python/resources/inspectionDescriptions/PySingleQuotedDocstringInspection.html @@ -0,0 +1,7 @@ + + + +This inspection highlights docstrings not using triple double-quoted string format. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyStatementEffectInspection.html b/python/resources/inspectionDescriptions/PyStatementEffectInspection.html new file mode 100644 index 000000000000..99eee87de619 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyStatementEffectInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects statements without any effect. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyStringExceptionInspection.html b/python/resources/inspectionDescriptions/PyStringExceptionInspection.html new file mode 100644 index 000000000000..c2584f6bd36c --- /dev/null +++ b/python/resources/inspectionDescriptions/PyStringExceptionInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects when a string exception is raised. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyStringFormatInspection.html b/python/resources/inspectionDescriptions/PyStringFormatInspection.html new file mode 100644 index 000000000000..2146cacc428f --- /dev/null +++ b/python/resources/inspectionDescriptions/PyStringFormatInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects errors in string formatting operations. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PySuperArgumentsInspection.html b/python/resources/inspectionDescriptions/PySuperArgumentsInspection.html new file mode 100644 index 000000000000..7229a01cfc75 --- /dev/null +++ b/python/resources/inspectionDescriptions/PySuperArgumentsInspection.html @@ -0,0 +1,7 @@ + + + + This inspection check that in any call to super(A, B), B either is an instance of A or a subclass of A. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyTrailingSemicolonInspection.html b/python/resources/inspectionDescriptions/PyTrailingSemicolonInspection.html new file mode 100644 index 000000000000..1d4b70b3dc13 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyTrailingSemicolonInspection.html @@ -0,0 +1,5 @@ + + +This inspection detects trailing semicolons in statements. + + diff --git a/python/resources/inspectionDescriptions/PyTupleAssignmentBalanceInspection.html b/python/resources/inspectionDescriptions/PyTupleAssignmentBalanceInspection.html new file mode 100644 index 000000000000..6aa175b0927f --- /dev/null +++ b/python/resources/inspectionDescriptions/PyTupleAssignmentBalanceInspection.html @@ -0,0 +1,6 @@ + + + + This inspection check that the number of expressions on right-hand side and targets on left-hand side are the same. + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyTupleItemAssignmentInspection.html b/python/resources/inspectionDescriptions/PyTupleItemAssignmentInspection.html new file mode 100644 index 000000000000..49a437b3561f --- /dev/null +++ b/python/resources/inspectionDescriptions/PyTupleItemAssignmentInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects assignments to tuple item. + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyTypeCheckerInspection.html b/python/resources/inspectionDescriptions/PyTypeCheckerInspection.html new file mode 100644 index 000000000000..f2fc714eee0c --- /dev/null +++ b/python/resources/inspectionDescriptions/PyTypeCheckerInspection.html @@ -0,0 +1,8 @@ + + +This inspection detects type errors in function call expressions. +Due to dynamic dispatch and duck typing, this is possible in a limited but +useful number of cases. Types of function parameters can be specified in +docstrings or in Python 3 function annotations. + + diff --git a/python/resources/inspectionDescriptions/PyUnboundLocalVariableInspection.html b/python/resources/inspectionDescriptions/PyUnboundLocalVariableInspection.html new file mode 100644 index 000000000000..c65de68ae122 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyUnboundLocalVariableInspection.html @@ -0,0 +1,5 @@ + + +This inspection warns about local variables referenced before assignment. + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyUnnecessaryBackslashInspection.html b/python/resources/inspectionDescriptions/PyUnnecessaryBackslashInspection.html new file mode 100644 index 000000000000..75d0bd3ba6d5 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyUnnecessaryBackslashInspection.html @@ -0,0 +1,7 @@ + + + +This inspection highlights backslashes in places where line continuation is implicit (inside (), [], {}). + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyUnreachableCodeInspection.html b/python/resources/inspectionDescriptions/PyUnreachableCodeInspection.html new file mode 100644 index 000000000000..ed689c3de81b --- /dev/null +++ b/python/resources/inspectionDescriptions/PyUnreachableCodeInspection.html @@ -0,0 +1,3 @@ + +This inspection detects code which can not be normally reached. + diff --git a/python/resources/inspectionDescriptions/PyUnresolvedReferencesInspection.html b/python/resources/inspectionDescriptions/PyUnresolvedReferencesInspection.html new file mode 100644 index 000000000000..8841b4687e61 --- /dev/null +++ b/python/resources/inspectionDescriptions/PyUnresolvedReferencesInspection.html @@ -0,0 +1,7 @@ + + +This inspection detects names that should resolve but don't. +Due to dynamic dispatch and duck typing, this is possible in a limited but useful +number of cases. Top-level and class-level items are supported better than instance items. + + diff --git a/python/resources/inspectionDescriptions/PyUnsupportedFeaturesInspection.html b/python/resources/inspectionDescriptions/PyUnsupportedFeaturesInspection.html new file mode 100644 index 000000000000..b0c92cf6725b --- /dev/null +++ b/python/resources/inspectionDescriptions/PyUnsupportedFeaturesInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects usage of language features which not supported by current Python version + + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyUnusedLocalInspection.html b/python/resources/inspectionDescriptions/PyUnusedLocalInspection.html new file mode 100644 index 000000000000..95ce583e518d --- /dev/null +++ b/python/resources/inspectionDescriptions/PyUnusedLocalInspection.html @@ -0,0 +1,3 @@ + + This inspection highlights local variables,parameters or local functions unused in scope. + diff --git a/python/resources/inspectionDescriptions/RestRoleInspection.html b/python/resources/inspectionDescriptions/RestRoleInspection.html new file mode 100644 index 000000000000..75809b6562be --- /dev/null +++ b/python/resources/inspectionDescriptions/RestRoleInspection.html @@ -0,0 +1,7 @@ + + + + This inspection detects undefined roles. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/ConvertFormatOperatorToMethodIntention/after.py.template b/python/resources/intentionDescriptions/ConvertFormatOperatorToMethodIntention/after.py.template new file mode 100644 index 000000000000..ea988e4ae394 --- /dev/null +++ b/python/resources/intentionDescriptions/ConvertFormatOperatorToMethodIntention/after.py.template @@ -0,0 +1 @@ +print("{0: 5.3F}".format(12)) \ No newline at end of file diff --git a/python/resources/intentionDescriptions/ConvertFormatOperatorToMethodIntention/before.py.template b/python/resources/intentionDescriptions/ConvertFormatOperatorToMethodIntention/before.py.template new file mode 100644 index 000000000000..fdf53299f13f --- /dev/null +++ b/python/resources/intentionDescriptions/ConvertFormatOperatorToMethodIntention/before.py.template @@ -0,0 +1 @@ +print("% 5.3F" % 12) diff --git a/python/resources/intentionDescriptions/ConvertFormatOperatorToMethodIntention/description.html b/python/resources/intentionDescriptions/ConvertFormatOperatorToMethodIntention/description.html new file mode 100644 index 000000000000..64adb58c79e3 --- /dev/null +++ b/python/resources/intentionDescriptions/ConvertFormatOperatorToMethodIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention replaces a string format operator with the equivalent 'format' method call. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/ConvertVariadicParamIntention/after.py.template b/python/resources/intentionDescriptions/ConvertVariadicParamIntention/after.py.template new file mode 100644 index 000000000000..4a19285fe1f5 --- /dev/null +++ b/python/resources/intentionDescriptions/ConvertVariadicParamIntention/after.py.template @@ -0,0 +1,2 @@ +def foo(foo): + doSomething(foo) \ No newline at end of file diff --git a/python/resources/intentionDescriptions/ConvertVariadicParamIntention/before.py.template b/python/resources/intentionDescriptions/ConvertVariadicParamIntention/before.py.template new file mode 100644 index 000000000000..ca5b415454d2 --- /dev/null +++ b/python/resources/intentionDescriptions/ConvertVariadicParamIntention/before.py.template @@ -0,0 +1,2 @@ +def foo(**kwargs): + doSomething(kwargs['foo']) \ No newline at end of file diff --git a/python/resources/intentionDescriptions/ConvertVariadicParamIntention/description.html b/python/resources/intentionDescriptions/ConvertVariadicParamIntention/description.html new file mode 100644 index 000000000000..10db640519f0 --- /dev/null +++ b/python/resources/intentionDescriptions/ConvertVariadicParamIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention replaces variadic parameter(s) with usual. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/ImportFromToImportIntention/after.py.template b/python/resources/intentionDescriptions/ImportFromToImportIntention/after.py.template new file mode 100644 index 000000000000..3158f1b50510 --- /dev/null +++ b/python/resources/intentionDescriptions/ImportFromToImportIntention/after.py.template @@ -0,0 +1,2 @@ +import sys +name = sys.argv[0] diff --git a/python/resources/intentionDescriptions/ImportFromToImportIntention/before.py.template b/python/resources/intentionDescriptions/ImportFromToImportIntention/before.py.template new file mode 100644 index 000000000000..8ccd26c2c6ae --- /dev/null +++ b/python/resources/intentionDescriptions/ImportFromToImportIntention/before.py.template @@ -0,0 +1,2 @@ +from sys import argv +name = argv[0] diff --git a/python/resources/intentionDescriptions/ImportFromToImportIntention/description.html b/python/resources/intentionDescriptions/ImportFromToImportIntention/description.html new file mode 100644 index 000000000000..96be99018b8f --- /dev/null +++ b/python/resources/intentionDescriptions/ImportFromToImportIntention/description.html @@ -0,0 +1,8 @@ + + + +This instenton transforms from module_name import ... into import module_name +and qualifies any names imported from that module by module name. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/ImportToImportFromIntention/after.py.template b/python/resources/intentionDescriptions/ImportToImportFromIntention/after.py.template new file mode 100644 index 000000000000..8ccd26c2c6ae --- /dev/null +++ b/python/resources/intentionDescriptions/ImportToImportFromIntention/after.py.template @@ -0,0 +1,2 @@ +from sys import argv +name = argv[0] diff --git a/python/resources/intentionDescriptions/ImportToImportFromIntention/before.py.template b/python/resources/intentionDescriptions/ImportToImportFromIntention/before.py.template new file mode 100644 index 000000000000..3158f1b50510 --- /dev/null +++ b/python/resources/intentionDescriptions/ImportToImportFromIntention/before.py.template @@ -0,0 +1,2 @@ +import sys +name = sys.argv[0] diff --git a/python/resources/intentionDescriptions/ImportToImportFromIntention/description.html b/python/resources/intentionDescriptions/ImportToImportFromIntention/description.html new file mode 100644 index 000000000000..880f1e8deea2 --- /dev/null +++ b/python/resources/intentionDescriptions/ImportToImportFromIntention/description.html @@ -0,0 +1,8 @@ + + + +This intention replaces import foo with from foo import ... +and drop qualifiers at references to names from foo. + + + diff --git a/python/resources/intentionDescriptions/ImportToggleAliasIntention/after.py.template b/python/resources/intentionDescriptions/ImportToggleAliasIntention/after.py.template new file mode 100644 index 000000000000..3331304b1da8 --- /dev/null +++ b/python/resources/intentionDescriptions/ImportToggleAliasIntention/after.py.template @@ -0,0 +1,2 @@ +from urllib import url2pathname as u2p +name = u2p("/") diff --git a/python/resources/intentionDescriptions/ImportToggleAliasIntention/before.py.template b/python/resources/intentionDescriptions/ImportToggleAliasIntention/before.py.template new file mode 100644 index 000000000000..6db525770030 --- /dev/null +++ b/python/resources/intentionDescriptions/ImportToggleAliasIntention/before.py.template @@ -0,0 +1,2 @@ +from urllib import url2pathname +name = url2pathname("/") diff --git a/python/resources/intentionDescriptions/ImportToggleAliasIntention/description.html b/python/resources/intentionDescriptions/ImportToggleAliasIntention/description.html new file mode 100644 index 000000000000..de5fc8167a1c --- /dev/null +++ b/python/resources/intentionDescriptions/ImportToggleAliasIntention/description.html @@ -0,0 +1,8 @@ + + + +This intention allows to add or remove an alias from an import element, renaming references accordingly. +Works both for import ... as ... and from ... import ... as statements. + + + diff --git a/python/resources/intentionDescriptions/PyConvertLambdaToFunctionIntention/after.py.template b/python/resources/intentionDescriptions/PyConvertLambdaToFunctionIntention/after.py.template new file mode 100644 index 000000000000..4429ea5a8361 --- /dev/null +++ b/python/resources/intentionDescriptions/PyConvertLambdaToFunctionIntention/after.py.template @@ -0,0 +1,7 @@ +def func2(x, y): + return (x+y)/y + +def func(seq): + ... + newlist = reduce(func2, seq) + ... diff --git a/python/resources/intentionDescriptions/PyConvertLambdaToFunctionIntention/before.py.template b/python/resources/intentionDescriptions/PyConvertLambdaToFunctionIntention/before.py.template new file mode 100644 index 000000000000..fd61cfd76461 --- /dev/null +++ b/python/resources/intentionDescriptions/PyConvertLambdaToFunctionIntention/before.py.template @@ -0,0 +1,4 @@ +def func(seq): + ... + newlist = reduce(lambda x, y: (x+y)/y, seq) + ... diff --git a/python/resources/intentionDescriptions/PyConvertLambdaToFunctionIntention/description.html b/python/resources/intentionDescriptions/PyConvertLambdaToFunctionIntention/description.html new file mode 100644 index 000000000000..2e58675974a3 --- /dev/null +++ b/python/resources/intentionDescriptions/PyConvertLambdaToFunctionIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention converts lambda function to function definition statement. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyConvertMethodToPropertyIntention/after.py.template b/python/resources/intentionDescriptions/PyConvertMethodToPropertyIntention/after.py.template new file mode 100644 index 000000000000..1c8040301451 --- /dev/null +++ b/python/resources/intentionDescriptions/PyConvertMethodToPropertyIntention/after.py.template @@ -0,0 +1,13 @@ +class MyClass(object): + """ + My class to show intention. + """ + def __init__(self): + self._x = None + + @property + def x(self): + return self._x + + +x = MyClass().x \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyConvertMethodToPropertyIntention/before.py.template b/python/resources/intentionDescriptions/PyConvertMethodToPropertyIntention/before.py.template new file mode 100644 index 000000000000..7940289de5cb --- /dev/null +++ b/python/resources/intentionDescriptions/PyConvertMethodToPropertyIntention/before.py.template @@ -0,0 +1,12 @@ +class MyClass(object): + """ + My class to show intention. + """ + def __init__(self): + self._x = None + + def x(self): + return self._x + + +x = MyClass().x() \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyConvertMethodToPropertyIntention/description.html b/python/resources/intentionDescriptions/PyConvertMethodToPropertyIntention/description.html new file mode 100644 index 000000000000..82ae5ebfe544 --- /dev/null +++ b/python/resources/intentionDescriptions/PyConvertMethodToPropertyIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts method to property. + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyConvertStaticMethodToFunctionIntention/after.py.template b/python/resources/intentionDescriptions/PyConvertStaticMethodToFunctionIntention/after.py.template new file mode 100644 index 000000000000..f393b5ef1e73 --- /dev/null +++ b/python/resources/intentionDescriptions/PyConvertStaticMethodToFunctionIntention/after.py.template @@ -0,0 +1,14 @@ +class MyClass(object): + """ + My class to show intention. + """ + + def __init__(self): + self.a = 1 + +def my_static_method(): + import code + import time + + time.sleep(100) + print code diff --git a/python/resources/intentionDescriptions/PyConvertStaticMethodToFunctionIntention/before.py.template b/python/resources/intentionDescriptions/PyConvertStaticMethodToFunctionIntention/before.py.template new file mode 100644 index 000000000000..a2e4b7985e54 --- /dev/null +++ b/python/resources/intentionDescriptions/PyConvertStaticMethodToFunctionIntention/before.py.template @@ -0,0 +1,15 @@ +class MyClass(object): + """ + My class to show intention. + """ + + def __init__(self): + self.a = 1 + + @staticmethod + def my_static_method(): + import code + import time + + time.sleep(100) + print code diff --git a/python/resources/intentionDescriptions/PyConvertStaticMethodToFunctionIntention/description.html b/python/resources/intentionDescriptions/PyConvertStaticMethodToFunctionIntention/description.html new file mode 100644 index 000000000000..ea7e64f10d6b --- /dev/null +++ b/python/resources/intentionDescriptions/PyConvertStaticMethodToFunctionIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts static method to function. + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyConvertTripleQuotedStringIntention/after.py.template b/python/resources/intentionDescriptions/PyConvertTripleQuotedStringIntention/after.py.template new file mode 100644 index 000000000000..af439511d107 --- /dev/null +++ b/python/resources/intentionDescriptions/PyConvertTripleQuotedStringIntention/after.py.template @@ -0,0 +1,3 @@ +a = ("This line is ok," "\n" + "but \"this\" includes far too much" "\n" + "whitespace at the start") \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyConvertTripleQuotedStringIntention/before.py.template b/python/resources/intentionDescriptions/PyConvertTripleQuotedStringIntention/before.py.template new file mode 100644 index 000000000000..b556824b48f0 --- /dev/null +++ b/python/resources/intentionDescriptions/PyConvertTripleQuotedStringIntention/before.py.template @@ -0,0 +1,3 @@ +a = """This line is ok, + but "this" includes far too much + whitespace at the start""" \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyConvertTripleQuotedStringIntention/description.html b/python/resources/intentionDescriptions/PyConvertTripleQuotedStringIntention/description.html new file mode 100644 index 000000000000..a75a51a90fbf --- /dev/null +++ b/python/resources/intentionDescriptions/PyConvertTripleQuotedStringIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention converts triple-quoted string to single-quoted. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyDemorganIntention/after.py.template b/python/resources/intentionDescriptions/PyDemorganIntention/after.py.template new file mode 100644 index 000000000000..de9e125e7530 --- /dev/null +++ b/python/resources/intentionDescriptions/PyDemorganIntention/after.py.template @@ -0,0 +1,2 @@ +if not (not a and not b): + pass \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyDemorganIntention/before.py.template b/python/resources/intentionDescriptions/PyDemorganIntention/before.py.template new file mode 100644 index 000000000000..1a0e3f300de4 --- /dev/null +++ b/python/resources/intentionDescriptions/PyDemorganIntention/before.py.template @@ -0,0 +1,2 @@ +if a or b: + pass \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyDemorganIntention/description.html b/python/resources/intentionDescriptions/PyDemorganIntention/description.html new file mode 100644 index 000000000000..4dfbca1bdc31 --- /dev/null +++ b/python/resources/intentionDescriptions/PyDemorganIntention/description.html @@ -0,0 +1,14 @@ + + + +This intention replaces either +
+a or b with not(not a and not b) or
+a and b with not(not a or not b)
+
+ + +inside boolean expression. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyDictConstructorToLiteralFormIntention/after.py.template b/python/resources/intentionDescriptions/PyDictConstructorToLiteralFormIntention/after.py.template new file mode 100644 index 000000000000..684a858f604c --- /dev/null +++ b/python/resources/intentionDescriptions/PyDictConstructorToLiteralFormIntention/after.py.template @@ -0,0 +1 @@ +{'a': 3, 'b': 5} \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyDictConstructorToLiteralFormIntention/before.py.template b/python/resources/intentionDescriptions/PyDictConstructorToLiteralFormIntention/before.py.template new file mode 100644 index 000000000000..2bfbfb98c348 --- /dev/null +++ b/python/resources/intentionDescriptions/PyDictConstructorToLiteralFormIntention/before.py.template @@ -0,0 +1 @@ +dict(a=3, b=5) \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyDictConstructorToLiteralFormIntention/description.html b/python/resources/intentionDescriptions/PyDictConstructorToLiteralFormIntention/description.html new file mode 100644 index 000000000000..ee5f619cd7f4 --- /dev/null +++ b/python/resources/intentionDescriptions/PyDictConstructorToLiteralFormIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention converts translation from dict constructor to dict literal form. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyDictLiteralFormToConstructorIntention/after.py.template b/python/resources/intentionDescriptions/PyDictLiteralFormToConstructorIntention/after.py.template new file mode 100644 index 000000000000..2bfbfb98c348 --- /dev/null +++ b/python/resources/intentionDescriptions/PyDictLiteralFormToConstructorIntention/after.py.template @@ -0,0 +1 @@ +dict(a=3, b=5) \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyDictLiteralFormToConstructorIntention/before.py.template b/python/resources/intentionDescriptions/PyDictLiteralFormToConstructorIntention/before.py.template new file mode 100644 index 000000000000..684a858f604c --- /dev/null +++ b/python/resources/intentionDescriptions/PyDictLiteralFormToConstructorIntention/before.py.template @@ -0,0 +1 @@ +{'a': 3, 'b': 5} \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyDictLiteralFormToConstructorIntention/description.html b/python/resources/intentionDescriptions/PyDictLiteralFormToConstructorIntention/description.html new file mode 100644 index 000000000000..9af5246228d3 --- /dev/null +++ b/python/resources/intentionDescriptions/PyDictLiteralFormToConstructorIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention converts translation from dict literal form to dict constructor. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyFlipComparisonIntention/after.py.template b/python/resources/intentionDescriptions/PyFlipComparisonIntention/after.py.template new file mode 100644 index 000000000000..37b79e3f1d30 --- /dev/null +++ b/python/resources/intentionDescriptions/PyFlipComparisonIntention/after.py.template @@ -0,0 +1,2 @@ +if a > b: + return a \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyFlipComparisonIntention/before.py.template b/python/resources/intentionDescriptions/PyFlipComparisonIntention/before.py.template new file mode 100644 index 000000000000..1814460d6f27 --- /dev/null +++ b/python/resources/intentionDescriptions/PyFlipComparisonIntention/before.py.template @@ -0,0 +1,2 @@ +if b < a: + return a \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyFlipComparisonIntention/description.html b/python/resources/intentionDescriptions/PyFlipComparisonIntention/description.html new file mode 100644 index 000000000000..c6b67f5acfe2 --- /dev/null +++ b/python/resources/intentionDescriptions/PyFlipComparisonIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention swaps the operands of a comparison expression. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyGenerateDocstringIntention/after.py.template b/python/resources/intentionDescriptions/PyGenerateDocstringIntention/after.py.template new file mode 100644 index 000000000000..bf0cffa3e944 --- /dev/null +++ b/python/resources/intentionDescriptions/PyGenerateDocstringIntention/after.py.template @@ -0,0 +1,7 @@ +def foo(a, b): + """ + :param a: + :param b: + :return: + """ + pass \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyGenerateDocstringIntention/before.py.template b/python/resources/intentionDescriptions/PyGenerateDocstringIntention/before.py.template new file mode 100644 index 000000000000..7898e8a5a596 --- /dev/null +++ b/python/resources/intentionDescriptions/PyGenerateDocstringIntention/before.py.template @@ -0,0 +1,2 @@ +def foo(a,b): + pass \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyGenerateDocstringIntention/description.html b/python/resources/intentionDescriptions/PyGenerateDocstringIntention/description.html new file mode 100644 index 000000000000..87e9bb7c4b80 --- /dev/null +++ b/python/resources/intentionDescriptions/PyGenerateDocstringIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention generates documentation string stub. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyJoinIfIntention/after.py.template b/python/resources/intentionDescriptions/PyJoinIfIntention/after.py.template new file mode 100644 index 000000000000..e2fcd5989953 --- /dev/null +++ b/python/resources/intentionDescriptions/PyJoinIfIntention/after.py.template @@ -0,0 +1,2 @@ +if a and b: + return \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyJoinIfIntention/before.py.template b/python/resources/intentionDescriptions/PyJoinIfIntention/before.py.template new file mode 100644 index 000000000000..c680e979fc94 --- /dev/null +++ b/python/resources/intentionDescriptions/PyJoinIfIntention/before.py.template @@ -0,0 +1,3 @@ +if a: + if b: + return \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyJoinIfIntention/description.html b/python/resources/intentionDescriptions/PyJoinIfIntention/description.html new file mode 100644 index 000000000000..794e24c6b87c --- /dev/null +++ b/python/resources/intentionDescriptions/PyJoinIfIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention converts two nested if statements into one if statement containing conjuction operation in it. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyNegateComparisonIntention/after.py.template b/python/resources/intentionDescriptions/PyNegateComparisonIntention/after.py.template new file mode 100644 index 000000000000..89ed0f8d31a2 --- /dev/null +++ b/python/resources/intentionDescriptions/PyNegateComparisonIntention/after.py.template @@ -0,0 +1,2 @@ +if a != b: + return a \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyNegateComparisonIntention/before.py.template b/python/resources/intentionDescriptions/PyNegateComparisonIntention/before.py.template new file mode 100644 index 000000000000..7283065cd014 --- /dev/null +++ b/python/resources/intentionDescriptions/PyNegateComparisonIntention/before.py.template @@ -0,0 +1,2 @@ +if not a == b: + return a \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyNegateComparisonIntention/description.html b/python/resources/intentionDescriptions/PyNegateComparisonIntention/description.html new file mode 100644 index 000000000000..acb11fef7ac6 --- /dev/null +++ b/python/resources/intentionDescriptions/PyNegateComparisonIntention/description.html @@ -0,0 +1,8 @@ + + + + This intention converts boolean comparison expression not(a negop b) into a op b. + Where op and negop are mutually inverse comparison operators, like == and !=. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyQuotedStringIntention/after.py.template b/python/resources/intentionDescriptions/PyQuotedStringIntention/after.py.template new file mode 100644 index 000000000000..cbf185626543 --- /dev/null +++ b/python/resources/intentionDescriptions/PyQuotedStringIntention/after.py.template @@ -0,0 +1 @@ +"\"Hello\" - said man." \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyQuotedStringIntention/before.py.template b/python/resources/intentionDescriptions/PyQuotedStringIntention/before.py.template new file mode 100644 index 000000000000..904c28a98492 --- /dev/null +++ b/python/resources/intentionDescriptions/PyQuotedStringIntention/before.py.template @@ -0,0 +1 @@ +'"Hello" - said man.' \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyQuotedStringIntention/description.html b/python/resources/intentionDescriptions/PyQuotedStringIntention/description.html new file mode 100644 index 000000000000..cfd35d664f82 --- /dev/null +++ b/python/resources/intentionDescriptions/PyQuotedStringIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention allows conversion between single-quoted and double-quoted strings. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PySplitIfIntention/after.py.template b/python/resources/intentionDescriptions/PySplitIfIntention/after.py.template new file mode 100644 index 000000000000..c0f7384ea7c6 --- /dev/null +++ b/python/resources/intentionDescriptions/PySplitIfIntention/after.py.template @@ -0,0 +1,3 @@ +if a: + if b: + return \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PySplitIfIntention/before.py.template b/python/resources/intentionDescriptions/PySplitIfIntention/before.py.template new file mode 100644 index 000000000000..9e841f5ee358 --- /dev/null +++ b/python/resources/intentionDescriptions/PySplitIfIntention/before.py.template @@ -0,0 +1,2 @@ +if a and b: + return \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PySplitIfIntention/description.html b/python/resources/intentionDescriptions/PySplitIfIntention/description.html new file mode 100644 index 000000000000..643c3c79571f --- /dev/null +++ b/python/resources/intentionDescriptions/PySplitIfIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention converts if statement containing conjuction operation into two nested if statements with simplified conditions. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyStringConcatenationToFormatIntention/after.py.template b/python/resources/intentionDescriptions/PyStringConcatenationToFormatIntention/after.py.template new file mode 100644 index 000000000000..56cd89aef1c8 --- /dev/null +++ b/python/resources/intentionDescriptions/PyStringConcatenationToFormatIntention/after.py.template @@ -0,0 +1 @@ +"a%sc" % (b) \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyStringConcatenationToFormatIntention/before.py.template b/python/resources/intentionDescriptions/PyStringConcatenationToFormatIntention/before.py.template new file mode 100644 index 000000000000..96db9539c962 --- /dev/null +++ b/python/resources/intentionDescriptions/PyStringConcatenationToFormatIntention/before.py.template @@ -0,0 +1 @@ +"a" + b + "c" \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyStringConcatenationToFormatIntention/description.html b/python/resources/intentionDescriptions/PyStringConcatenationToFormatIntention/description.html new file mode 100644 index 000000000000..908daa21be31 --- /dev/null +++ b/python/resources/intentionDescriptions/PyStringConcatenationToFormatIntention/description.html @@ -0,0 +1,8 @@ + + + + This intention replaces a string concatenation with the equivalent expression with string formatting operator usage + or with str.format() method call (for Python versions >= 2.7). + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyTransformConditionalExpressionIntention/after.py.template b/python/resources/intentionDescriptions/PyTransformConditionalExpressionIntention/after.py.template new file mode 100644 index 000000000000..24e157d011dc --- /dev/null +++ b/python/resources/intentionDescriptions/PyTransformConditionalExpressionIntention/after.py.template @@ -0,0 +1,4 @@ +if cond: + x = a +else: + x = b \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyTransformConditionalExpressionIntention/before.py.template b/python/resources/intentionDescriptions/PyTransformConditionalExpressionIntention/before.py.template new file mode 100644 index 000000000000..601e958f079d --- /dev/null +++ b/python/resources/intentionDescriptions/PyTransformConditionalExpressionIntention/before.py.template @@ -0,0 +1 @@ +x = a if cond else b \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyTransformConditionalExpressionIntention/description.html b/python/resources/intentionDescriptions/PyTransformConditionalExpressionIntention/description.html new file mode 100644 index 000000000000..c0552efb32e6 --- /dev/null +++ b/python/resources/intentionDescriptions/PyTransformConditionalExpressionIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention transforms conditional expression into if/else statement. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyYieldFromIntention/after.py.template b/python/resources/intentionDescriptions/PyYieldFromIntention/after.py.template new file mode 100644 index 000000000000..773e592b09e0 --- /dev/null +++ b/python/resources/intentionDescriptions/PyYieldFromIntention/after.py.template @@ -0,0 +1 @@ +yield from b \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyYieldFromIntention/before.py.template b/python/resources/intentionDescriptions/PyYieldFromIntention/before.py.template new file mode 100644 index 000000000000..aa447f1e3b13 --- /dev/null +++ b/python/resources/intentionDescriptions/PyYieldFromIntention/before.py.template @@ -0,0 +1,2 @@ +for a in b: + yield a \ No newline at end of file diff --git a/python/resources/intentionDescriptions/PyYieldFromIntention/description.html b/python/resources/intentionDescriptions/PyYieldFromIntention/description.html new file mode 100644 index 000000000000..667a76164b3f --- /dev/null +++ b/python/resources/intentionDescriptions/PyYieldFromIntention/description.html @@ -0,0 +1,5 @@ + + + This intention transforms explicit iteration with 'yield' into 'yield from'. + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/ReplaceListComprehensionWithForIntention/after.py.template b/python/resources/intentionDescriptions/ReplaceListComprehensionWithForIntention/after.py.template new file mode 100644 index 000000000000..036345d596d1 --- /dev/null +++ b/python/resources/intentionDescriptions/ReplaceListComprehensionWithForIntention/after.py.template @@ -0,0 +1,3 @@ +a = [] +for x in xrange(5): + a.add(x) \ No newline at end of file diff --git a/python/resources/intentionDescriptions/ReplaceListComprehensionWithForIntention/before.py.template b/python/resources/intentionDescriptions/ReplaceListComprehensionWithForIntention/before.py.template new file mode 100644 index 000000000000..eef9ef7bb5dd --- /dev/null +++ b/python/resources/intentionDescriptions/ReplaceListComprehensionWithForIntention/before.py.template @@ -0,0 +1 @@ +a = [x for x in xrange(5)] \ No newline at end of file diff --git a/python/resources/intentionDescriptions/ReplaceListComprehensionWithForIntention/description.html b/python/resources/intentionDescriptions/ReplaceListComprehensionWithForIntention/description.html new file mode 100644 index 000000000000..54da12726567 --- /dev/null +++ b/python/resources/intentionDescriptions/ReplaceListComprehensionWithForIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention replaces a list comprehension with for loop. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/SpecifyTypeInDocstringIntention/after.html.template b/python/resources/intentionDescriptions/SpecifyTypeInDocstringIntention/after.html.template new file mode 100644 index 000000000000..489da3518483 --- /dev/null +++ b/python/resources/intentionDescriptions/SpecifyTypeInDocstringIntention/after.html.template @@ -0,0 +1,5 @@ +def foo(var): + """ + @type var: string + """ + var. \ No newline at end of file diff --git a/python/resources/intentionDescriptions/SpecifyTypeInDocstringIntention/before.html.template b/python/resources/intentionDescriptions/SpecifyTypeInDocstringIntention/before.html.template new file mode 100644 index 000000000000..fcdfce0ebee8 --- /dev/null +++ b/python/resources/intentionDescriptions/SpecifyTypeInDocstringIntention/before.html.template @@ -0,0 +1,2 @@ +def foo(var): + var. \ No newline at end of file diff --git a/python/resources/intentionDescriptions/SpecifyTypeInDocstringIntention/description.html b/python/resources/intentionDescriptions/SpecifyTypeInDocstringIntention/description.html new file mode 100644 index 000000000000..9ff83f3b28bc --- /dev/null +++ b/python/resources/intentionDescriptions/SpecifyTypeInDocstringIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention used to specify type for expression in docstring. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/SpecifyTypeInPy3AnnotationsIntention/after.html.template b/python/resources/intentionDescriptions/SpecifyTypeInPy3AnnotationsIntention/after.html.template new file mode 100644 index 000000000000..5f5265fc0bd6 --- /dev/null +++ b/python/resources/intentionDescriptions/SpecifyTypeInPy3AnnotationsIntention/after.html.template @@ -0,0 +1,2 @@ +def foo(var: object): + var. \ No newline at end of file diff --git a/python/resources/intentionDescriptions/SpecifyTypeInPy3AnnotationsIntention/before.html.template b/python/resources/intentionDescriptions/SpecifyTypeInPy3AnnotationsIntention/before.html.template new file mode 100644 index 000000000000..fcdfce0ebee8 --- /dev/null +++ b/python/resources/intentionDescriptions/SpecifyTypeInPy3AnnotationsIntention/before.html.template @@ -0,0 +1,2 @@ +def foo(var): + var. \ No newline at end of file diff --git a/python/resources/intentionDescriptions/SpecifyTypeInPy3AnnotationsIntention/description.html b/python/resources/intentionDescriptions/SpecifyTypeInPy3AnnotationsIntention/description.html new file mode 100644 index 000000000000..dce9825a7ef6 --- /dev/null +++ b/python/resources/intentionDescriptions/SpecifyTypeInPy3AnnotationsIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention used to specify type for expression in Python 3 using type annotations. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/TypeAssertionIntention/after.html.template b/python/resources/intentionDescriptions/TypeAssertionIntention/after.html.template new file mode 100644 index 000000000000..fd8ffc7fb452 --- /dev/null +++ b/python/resources/intentionDescriptions/TypeAssertionIntention/after.html.template @@ -0,0 +1,3 @@ +var = unknown() +assert isinstance(var, object) +var. \ No newline at end of file diff --git a/python/resources/intentionDescriptions/TypeAssertionIntention/before.html.template b/python/resources/intentionDescriptions/TypeAssertionIntention/before.html.template new file mode 100644 index 000000000000..3ce25401f353 --- /dev/null +++ b/python/resources/intentionDescriptions/TypeAssertionIntention/before.html.template @@ -0,0 +1,2 @@ +var = unknown() +var. \ No newline at end of file diff --git a/python/resources/intentionDescriptions/TypeAssertionIntention/description.html b/python/resources/intentionDescriptions/TypeAssertionIntention/description.html new file mode 100644 index 000000000000..be57cd3b6847 --- /dev/null +++ b/python/resources/intentionDescriptions/TypeAssertionIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention used to specify type for expression by assertion. + + + \ No newline at end of file diff --git a/python/resources/intentionDescriptions/WrapTransTagIntention/after.html.template b/python/resources/intentionDescriptions/WrapTransTagIntention/after.html.template new file mode 100644 index 000000000000..d26fc1258f3e --- /dev/null +++ b/python/resources/intentionDescriptions/WrapTransTagIntention/after.html.template @@ -0,0 +1 @@ +{% blocktrans %}This will have {{ myvar }} inside.{% endblocktrans %} \ No newline at end of file diff --git a/python/resources/intentionDescriptions/WrapTransTagIntention/before.html.template b/python/resources/intentionDescriptions/WrapTransTagIntention/before.html.template new file mode 100644 index 000000000000..e7f818a2378b --- /dev/null +++ b/python/resources/intentionDescriptions/WrapTransTagIntention/before.html.template @@ -0,0 +1 @@ +This will have {{ myvar }} inside. \ No newline at end of file diff --git a/python/resources/intentionDescriptions/WrapTransTagIntention/description.html b/python/resources/intentionDescriptions/WrapTransTagIntention/description.html new file mode 100644 index 000000000000..6646903b6258 --- /dev/null +++ b/python/resources/intentionDescriptions/WrapTransTagIntention/description.html @@ -0,0 +1,7 @@ + + + + This intention wraps selected template part with {% trans %} tag. + + + \ No newline at end of file diff --git a/python/resources/liveTemplates/Python.xml b/python/resources/liveTemplates/Python.xml new file mode 100644 index 000000000000..314f81c6cfb9 --- /dev/null +++ b/python/resources/liveTemplates/Python.xml @@ -0,0 +1,24 @@ + + + + + diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 528183691f29..a6c3ac9c83a8 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -3,8 +3,6 @@ - com.jetbrains.python.PyBundle - +