diff --git a/python/build/python_plugin_build.gant b/python/build/python_plugin_build.gant index 82b2ce53c9be..d7bee89e7ca8 100644 --- a/python/build/python_plugin_build.gant +++ b/python/build/python_plugin_build.gant @@ -277,6 +277,7 @@ target(name: "compile", description: "Compile module python") { include(name: "pydevSrc") include(name: "openapi/src") include(name: "psi-api/src") + include(name: "community/src") include(name: "pluginResources") } connectorPlugins.each { @@ -328,6 +329,10 @@ target(name: "compile", description: "Compile module python") { patternset(refid: "resources.pt") type(type: "file") } + fileset(dir: "${pluginHome}/community/src") { + patternset(refid: "resources.pt") + type(type: "file") + } connectorPlugins.each { fileset(dir: "${pluginHome}/${it}/resources") { patternset(refid: "resources.pt") diff --git a/python/layout.xml b/python/layout.xml index b4386857b114..2b35430d16b3 100644 --- a/python/layout.xml +++ b/python/layout.xml @@ -15,6 +15,7 @@ + diff --git a/python/pluginSrc/META-INF/plugin.xml b/python/pluginSrc/META-INF/plugin.xml index d14b1e7f6070..9b613f34176d 100644 --- a/python/pluginSrc/META-INF/plugin.xml +++ b/python/pluginSrc/META-INF/plugin.xml @@ -4,9 +4,9 @@ Pythonid Python - + Smart editing for Python scripts - 2.11 + 2.11.2 com.intellij.modules.ultimate com.intellij.modules.java Coverage diff --git a/python/psi-api/src/com/jetbrains/python/PythonDialectsTokenSetContributor.java b/python/psi-api/src/com/jetbrains/python/PythonDialectsTokenSetContributor.java index 5a7ef32790ba..798ec70e421f 100644 --- a/python/psi-api/src/com/jetbrains/python/PythonDialectsTokenSetContributor.java +++ b/python/psi-api/src/com/jetbrains/python/PythonDialectsTokenSetContributor.java @@ -15,4 +15,6 @@ public interface PythonDialectsTokenSetContributor { TokenSet getKeywordTokens(); TokenSet getParameterTokens(); TokenSet getFunctionDeclarationTokens(); + TokenSet getUnbalancedBracesRecoveryTokens(); + TokenSet getReferenceExpressionTokens(); } diff --git a/python/psi-api/src/com/jetbrains/python/inspections/PyInspectionExtension.java b/python/psi-api/src/com/jetbrains/python/inspections/PyInspectionExtension.java new file mode 100644 index 000000000000..635cf96ccbc9 --- /dev/null +++ b/python/psi-api/src/com/jetbrains/python/inspections/PyInspectionExtension.java @@ -0,0 +1,26 @@ +package com.jetbrains.python.inspections; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.psi.PsiElement; +import com.jetbrains.python.psi.PyDocStringOwner; + +import java.util.List; + +/** + * @author yole + */ +public abstract class PyInspectionExtension { + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("Pythonid.inspectionExtension"); + + public boolean ignoreUnused(PsiElement local) { + return false; + } + + public boolean ignoreMissingDocstring(PyDocStringOwner docStringOwner) { + return false; + } + + public List getFunctionParametersFromUsage(PsiElement elt) { + return null; + } +} diff --git a/python/psi-api/src/com/jetbrains/python/psi/NameDefiner.java b/python/psi-api/src/com/jetbrains/python/psi/NameDefiner.java index f5537c7d0c44..870ca00a8a62 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/NameDefiner.java +++ b/python/psi-api/src/com/jetbrains/python/psi/NameDefiner.java @@ -5,10 +5,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** - * PSI element that (re)defnies names in following namespace, e.g. as assignment statement does. + * PSI element that (re)defines names in following namespace, e.g. as assignment statement does. * * NOTE: When declaring additional elements as name definers, please also adjust the token set via - * {@link PythonDialectsTokenSetContributor#getNameDefinerTokens()}. + * {@link com.jetbrains.python.PythonDialectsTokenSetContributor#getNameDefinerTokens()}. * * @author dcheryasov */ diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyImportElement.java b/python/psi-api/src/com/jetbrains/python/psi/PyImportElement.java index d53f5f2f93a9..b0ba10eced94 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyImportElement.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyImportElement.java @@ -9,7 +9,7 @@ import org.jetbrains.annotations.Nullable; /** * @author yole */ -public interface PyImportElement extends PyElement, NameDefiner, StubBasedPsiElement { +public interface PyImportElement extends PyElement, PyImportedNameDefiner, StubBasedPsiElement { @Nullable PyReferenceExpression getImportReferenceExpression(); diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyImportedNameDefiner.java b/python/psi-api/src/com/jetbrains/python/psi/PyImportedNameDefiner.java new file mode 100644 index 000000000000..8081d0fd38d9 --- /dev/null +++ b/python/psi-api/src/com/jetbrains/python/psi/PyImportedNameDefiner.java @@ -0,0 +1,9 @@ +package com.jetbrains.python.psi; + +/** + * Name definer that defines names imported somehow from other modules. + * + * @author vlan + */ +public interface PyImportedNameDefiner extends NameDefiner { +} diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyStarImportElement.java b/python/psi-api/src/com/jetbrains/python/psi/PyStarImportElement.java index 1f91048da631..1a00a21c17d9 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyStarImportElement.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyStarImportElement.java @@ -5,5 +5,5 @@ package com.jetbrains.python.psi; * User: dcheryasov * Date: Jul 28, 2008 */ -public interface PyStarImportElement extends PyElement, NameDefiner { +public interface PyStarImportElement extends PyElement, PyImportedNameDefiner { } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PyPositionConverter.java b/python/pydevSrc/com/jetbrains/python/debugger/PyPositionConverter.java index 2b44f9d9ff99..8fcdc5664e0e 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/PyPositionConverter.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyPositionConverter.java @@ -15,4 +15,6 @@ public interface PyPositionConverter { @Nullable XSourcePosition convertFromPython(@NotNull final PySourcePosition position); + + PySignature convertSignature(PySignature signature); } diff --git a/python/rest/resources/META-INF/plugin.xml b/python/rest/resources/META-INF/plugin.xml index d80ed0ea468e..5a75d1bd0033 100644 --- a/python/rest/resources/META-INF/plugin.xml +++ b/python/rest/resources/META-INF/plugin.xml @@ -4,7 +4,7 @@ This plugin enables support for reStructuredText files (*.rst) JetBrains 130.SNAPSHOT - + com.intellij.modules.lang diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 2aca6649cb00..b7a25ca8218f 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -456,7 +456,7 @@ - + @@ -686,6 +686,7 @@ + @@ -893,7 +894,7 @@ - + @@ -912,6 +913,7 @@ + @@ -946,7 +948,7 @@ - + @@ -954,6 +956,7 @@ + diff --git a/python/src/com/jetbrains/python/PyElementTypes.java b/python/src/com/jetbrains/python/PyElementTypes.java index a76d47d1184e..7b58d74ac685 100644 --- a/python/src/com/jetbrains/python/PyElementTypes.java +++ b/python/src/com/jetbrains/python/PyElementTypes.java @@ -72,8 +72,6 @@ public interface PyElementTypes { PyElementType EMPTY_EXPRESSION = new PyElementType("EMPTY_EXPRESSION", PyEmptyExpressionImpl.class); PyElementType REFERENCE_EXPRESSION = new PyElementType("REFERENCE_EXPRESSION", PyReferenceExpressionImpl.class); - TokenSet REFERENCE_EXPRESSION_SET = TokenSet.create(REFERENCE_EXPRESSION); - PyStubElementType TARGET_EXPRESSION = new PyTargetExpressionElementType(); PyElementType INTEGER_LITERAL_EXPRESSION = new PyElementType("INTEGER_LITERAL_EXPRESSION", PyNumericLiteralExpressionImpl.class); PyElementType FLOAT_LITERAL_EXPRESSION = new PyElementType("FLOAT_LITERAL_EXPRESSION", PyNumericLiteralExpressionImpl.class); diff --git a/python/src/com/jetbrains/python/PythonDialectsTokenSetProvider.java b/python/src/com/jetbrains/python/PythonDialectsTokenSetProvider.java index 73375523ad99..1e0da69a7807 100644 --- a/python/src/com/jetbrains/python/PythonDialectsTokenSetProvider.java +++ b/python/src/com/jetbrains/python/PythonDialectsTokenSetProvider.java @@ -15,6 +15,8 @@ public class PythonDialectsTokenSetProvider { private final TokenSet myKeywordTokens; private final TokenSet myParameterTokens; private final TokenSet myFunctionDeclarationTokens; + private final TokenSet myUnbalancedBracesRecoveryTokens; + private final TokenSet myReferenceExpressionTokens; private PythonDialectsTokenSetProvider() { TokenSet stmts = TokenSet.EMPTY; @@ -23,6 +25,8 @@ public class PythonDialectsTokenSetProvider { TokenSet keywords = TokenSet.EMPTY; TokenSet parameters = TokenSet.EMPTY; TokenSet functionDeclarations = TokenSet.EMPTY; + TokenSet recoveryTokens = TokenSet.EMPTY; + TokenSet referenceExpressions = TokenSet.EMPTY; for(PythonDialectsTokenSetContributor contributor: Extensions.getExtensions(PythonDialectsTokenSetContributor.EP_NAME)) { stmts = TokenSet.orSet(stmts, contributor.getStatementTokens()); exprs = TokenSet.orSet(exprs, contributor.getExpressionTokens()); @@ -30,6 +34,8 @@ public class PythonDialectsTokenSetProvider { keywords = TokenSet.orSet(keywords, contributor.getKeywordTokens()); parameters = TokenSet.orSet(parameters, contributor.getParameterTokens()); functionDeclarations = TokenSet.orSet(functionDeclarations, contributor.getFunctionDeclarationTokens()); + recoveryTokens = TokenSet.orSet(recoveryTokens, contributor.getUnbalancedBracesRecoveryTokens()); + referenceExpressions = TokenSet.orSet(referenceExpressions, contributor.getReferenceExpressionTokens()); } myStatementTokens = stmts; myExpressionTokens = exprs; @@ -37,6 +43,8 @@ public class PythonDialectsTokenSetProvider { myKeywordTokens = keywords; myParameterTokens = parameters; myFunctionDeclarationTokens = functionDeclarations; + myUnbalancedBracesRecoveryTokens = recoveryTokens; + myReferenceExpressionTokens = referenceExpressions; } public TokenSet getStatementTokens() { @@ -62,4 +70,12 @@ public class PythonDialectsTokenSetProvider { public TokenSet getFunctionDeclarationTokens() { return myFunctionDeclarationTokens; } + + public TokenSet getUnbalancedBracesRecoveryTokens() { + return myUnbalancedBracesRecoveryTokens; + } + + public TokenSet getReferenceExpressionTokens() { + return myReferenceExpressionTokens; + } } diff --git a/python/src/com/jetbrains/python/PythonTokenSetContributor.java b/python/src/com/jetbrains/python/PythonTokenSetContributor.java index 4e1165a77868..878043dcb078 100644 --- a/python/src/com/jetbrains/python/PythonTokenSetContributor.java +++ b/python/src/com/jetbrains/python/PythonTokenSetContributor.java @@ -64,4 +64,15 @@ public class PythonTokenSetContributor implements PythonDialectsTokenSetContribu public TokenSet getFunctionDeclarationTokens() { return TokenSet.create(FUNCTION_DECLARATION); } + + @Override + public TokenSet getUnbalancedBracesRecoveryTokens() { + return TokenSet.create(DEF_KEYWORD, CLASS_KEYWORD, RETURN_KEYWORD, WITH_KEYWORD, WHILE_KEYWORD, BREAK_KEYWORD, CONTINUE_KEYWORD, + RAISE_KEYWORD, TRY_KEYWORD, EXCEPT_KEYWORD, FINALLY_KEYWORD); + } + + @Override + public TokenSet getReferenceExpressionTokens() { + return TokenSet.create(REFERENCE_EXPRESSION); + } } diff --git a/python/src/com/jetbrains/python/buildout/BuildoutFacet.java b/python/src/com/jetbrains/python/buildout/BuildoutFacet.java index 5fae6a5be5f7..fb543e974811 100644 --- a/python/src/com/jetbrains/python/buildout/BuildoutFacet.java +++ b/python/src/com/jetbrains/python/buildout/BuildoutFacet.java @@ -236,6 +236,11 @@ public class BuildoutFacet extends Facet implements return cfg.getPaths(); } + @Override + public boolean acceptRootAsTopLevelPackage() { + return false; + } + @Nullable public static BuildoutFacet getInstance(Module module) { return FacetManager.getInstance(module).getFacetByType(BuildoutFacetType.ID); diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java index d4aeb3f752c8..4882c509e157 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java @@ -3,7 +3,7 @@ package com.jetbrains.python.codeInsight.dataflow.scope; import com.intellij.codeInsight.dataflow.DFALimitExceededException; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiNamedElement; -import com.jetbrains.python.psi.NameDefiner; +import com.jetbrains.python.psi.PyImportedNameDefiner; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,7 +27,7 @@ public interface Scope { boolean containsDeclaration(String name); @NotNull - List getNameDefiners(); + List getImportedNameDefiners(); @Nullable PsiNamedElement getNamedElement(String name); diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java index 68ce31d527be..ab86fac9b9bb 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java @@ -6,7 +6,6 @@ import com.intellij.codeInsight.dataflow.map.DFAMap; import com.intellij.codeInsight.dataflow.map.DFAMapEngine; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiNamedElement; -import com.jetbrains.cython.psi.CythonIncludeStatement; import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.dataflow.PyReachingDefsDfaInstance; @@ -31,7 +30,7 @@ public class ScopeImpl implements Scope { private volatile List myNestedScopes; private final ScopeOwner myFlowOwner; private volatile Map myNamedElements; - private volatile List myNameDefiners; // declarations which declare unknown set of names, such as 'from ... import *' + private volatile List myImportedNameDefiners; // Declarations which declare unknown set of imported names private volatile Set myAugAssignments; public ScopeImpl(final ScopeOwner flowOwner) { @@ -97,7 +96,7 @@ public class ScopeImpl implements Scope { } public boolean containsDeclaration(final String name) { - if (myNamedElements == null || myNameDefiners == null) { + if (myNamedElements == null || myImportedNameDefiners == null) { collectDeclarations(); } if (isNonlocal(name)) { @@ -109,7 +108,7 @@ public class ScopeImpl implements Scope { if (isAugAssignment(name)) { return true; } - for (NameDefiner definer : getNameDefiners()) { + for (NameDefiner definer : getImportedNameDefiners()) { if (definer.getElementNamed(name) != null) { return true; } @@ -119,11 +118,11 @@ public class ScopeImpl implements Scope { @NotNull @Override - public List getNameDefiners() { - if (myNameDefiners == null) { + public List getImportedNameDefiners() { + if (myImportedNameDefiners == null) { collectDeclarations(); } - return myNameDefiners; + return myImportedNameDefiners; } @Nullable @@ -158,7 +157,7 @@ public class ScopeImpl implements Scope { private void collectDeclarations() { final Map namedElements = new HashMap(); - final List nameDefiners = new ArrayList(); + final List importedNameDefiners = new ArrayList(); final List nestedScopes = new ArrayList(); final Set globals = new HashSet(); final Set nonlocals = new HashSet(); @@ -214,9 +213,8 @@ public class ScopeImpl implements Scope { if (node instanceof PsiNamedElement && !(node instanceof PyKeywordArgument)) { namedElements.put(node.getName(), (PsiNamedElement)node); } - // TODO: Cython-specific code - if (node instanceof PyStarImportElement || node instanceof PyImportElement || node instanceof CythonIncludeStatement) { - nameDefiners.add((NameDefiner)node); + if (node instanceof PyImportedNameDefiner) { + importedNameDefiners.add((PyImportedNameDefiner)node); } if (node instanceof ScopeOwner) { final Scope scope = ControlFlowCache.getScope((ScopeOwner)node); @@ -228,7 +226,7 @@ public class ScopeImpl implements Scope { } }); - Collections.sort(nameDefiners, new Comparator() { + Collections.sort(importedNameDefiners, new Comparator() { @Override public int compare(NameDefiner d1, NameDefiner d2) { return getPriority(d2) - getPriority(d1); @@ -243,7 +241,7 @@ public class ScopeImpl implements Scope { }); myNamedElements = namedElements; - myNameDefiners = nameDefiners; + myImportedNameDefiners = importedNameDefiners; myNestedScopes = nestedScopes; myGlobals = globals; myNonlocals = nonlocals; diff --git a/python/src/com/jetbrains/python/console/PyConsoleOptions.java b/python/src/com/jetbrains/python/console/PyConsoleOptions.java new file mode 100644 index 000000000000..187877daae84 --- /dev/null +++ b/python/src/com/jetbrains/python/console/PyConsoleOptions.java @@ -0,0 +1,160 @@ +package com.jetbrains.python.console; + +import com.google.common.collect.Maps; +import com.intellij.openapi.components.*; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.util.containers.ComparatorUtil; +import com.jetbrains.python.run.AbstractPyCommonOptionsForm; + +import java.util.Map; + +/** + * @author traff + */ +@State( + name = "PyConsoleOptionsProvider", + storages = { + @Storage(file = StoragePathMacros.WORKSPACE_FILE) + } +) +public class PyConsoleOptions implements PersistentStateComponent { + private State myState = new State(); + + public PyConsoleSettings getPythonConsoleSettings() { + return myState.myPythonConsoleState; + } + + public boolean isShowDebugConsoleByDefault() { + return myState.myShowDebugConsoleByDefault; + } + + public void setShowDebugConsoleByDefault(boolean showDebugConsoleByDefault) { + myState.myShowDebugConsoleByDefault = showDebugConsoleByDefault; + } + + public boolean isShowSeparatorLine() { + return myState.myShowSeparatorLine; + } + + public void setShowSeparatorLine(boolean showSeparatorLine) { + myState.myShowSeparatorLine = showSeparatorLine; + } + + + public static PyConsoleOptions getInstance(Project project) { + return ServiceManager.getService(project, PyConsoleOptions.class); + } + + @Override + public State getState() { + return myState; + } + + @Override + public void loadState(State state) { + myState.myShowDebugConsoleByDefault = state.myShowDebugConsoleByDefault; + myState.myShowSeparatorLine = state.myShowSeparatorLine; + myState.myPythonConsoleState = state.myPythonConsoleState; + } + + public static class State { + public PyConsoleSettings myPythonConsoleState = new PyConsoleSettings(); + + public boolean myShowDebugConsoleByDefault = false; + public boolean myShowSeparatorLine = true; + } + + public static class PyConsoleSettings { + public String myCustomStartScript = ""; + public String mySdkHome = null; + public String myInterpreterOptions = ""; + public boolean myUseModuleSdk; + public String myModuleName = null; + public Map myEnvs = Maps.newHashMap(); + public String myWorkingDirectory = ""; + public boolean myAddContentRoots = true; + public boolean myAddSourceRoots; + + public String getCustomStartScript() { + return myCustomStartScript; + } + + public String getSdkHome() { + return mySdkHome; + } + + public void apply(AbstractPyCommonOptionsForm form) { + mySdkHome = form.getSdkHome(); + myInterpreterOptions = form.getInterpreterOptions(); + myEnvs = form.getEnvs(); + myUseModuleSdk = form.isUseModuleSdk(); + myModuleName = form.getModule() == null ? null : form.getModule().getName(); + myWorkingDirectory = form.getWorkingDirectory(); + + myAddContentRoots = form.addContentRoots(); + myAddSourceRoots = form.addSourceRoots(); + } + + public boolean isModified(AbstractPyCommonOptionsForm form) { + return !ComparatorUtil.equalsNullable(mySdkHome, form.getSdkHome()) || + !myInterpreterOptions.equals(form.getInterpreterOptions()) || + !myEnvs.equals(form.getEnvs()) || + myUseModuleSdk != form.isUseModuleSdk() || + myAddContentRoots != form.addContentRoots() || + myAddSourceRoots != form.addSourceRoots() + || !ComparatorUtil.equalsNullable(myModuleName, form.getModule() == null ? null : form.getModule().getName()) + || !myWorkingDirectory.equals(form.getWorkingDirectory()); + } + + public void reset(Project project, AbstractPyCommonOptionsForm form) { + form.setEnvs(myEnvs); + form.setInterpreterOptions(myInterpreterOptions); + form.setSdkHome(mySdkHome); + form.setUseModuleSdk(myUseModuleSdk); + form.addContentRoots(myAddContentRoots); + form.addSourceRoots(myAddSourceRoots); + boolean moduleWasAutoselected = false; + if (form.isUseModuleSdk() != myUseModuleSdk) { + myUseModuleSdk = form.isUseModuleSdk(); + moduleWasAutoselected = true; + } + + if (myModuleName != null) { + form.setModule(ModuleManager.getInstance(project).findModuleByName(myModuleName)); + } + + if (moduleWasAutoselected && form.getModule() != null) { + myModuleName = form.getModule().getName(); + } + + form.setWorkingDirectory(form.getWorkingDirectory()); + } + + public String getModuleName() { + return myModuleName; + } + + public String getWorkingDirectory() { + return myWorkingDirectory; + } + + public boolean isUseModuleSdk() { + return myUseModuleSdk; + } + + public Map getEnvs() { + return myEnvs; + } + + public boolean addContentRoots() { + return myAddContentRoots; + } + + public boolean addSourceRoots() { + return myAddSourceRoots; + } + + } +} + diff --git a/python/src/com/jetbrains/python/console/PyConsoleOptionsConfigurable.java b/python/src/com/jetbrains/python/console/PyConsoleOptionsConfigurable.java index 2ce61dfd8475..7ca7bf245c7d 100644 --- a/python/src/com/jetbrains/python/console/PyConsoleOptionsConfigurable.java +++ b/python/src/com/jetbrains/python/console/PyConsoleOptionsConfigurable.java @@ -1,12 +1,12 @@ package com.jetbrains.python.console; import com.google.common.collect.Lists; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.project.Project; import com.intellij.ui.components.JBCheckBox; -import com.jetbrains.django.facet.DjangoFacet; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -19,17 +19,14 @@ import java.util.List; public class PyConsoleOptionsConfigurable extends SearchableConfigurable.Parent.Abstract implements Configurable.NoScroll{ public static final String CONSOLE_SETTINGS_HELP_REFERENCE = "reference.project.settings.console"; public static final String CONSOLE_SETTINGS_HELP_REFERENCE_PYTHON = "reference.project.settings.console.python"; - public static final String CONSOLE_SETTINGS_HELP_REFERENCE_DJANGO = "reference.project.settings.console.django"; private PyConsoleOptionsPanel myPanel; - private PyConsoleSpecificOptionsPanel myPythonConsoleOptionsPanel; - private PyConsoleSpecificOptionsPanel myDjangoConsoleOptionsPanel; - private final PyConsoleOptionsProvider myOptionsProvider; + private final PyConsoleOptions myOptionsProvider; private Project myProject; - public PyConsoleOptionsConfigurable(PyConsoleOptionsProvider optionsProvider, Project project) { + public PyConsoleOptionsConfigurable(PyConsoleOptions optionsProvider, Project project) { myOptionsProvider = optionsProvider; myProject = project; } @@ -49,24 +46,25 @@ public class PyConsoleOptionsConfigurable extends SearchableConfigurable.Parent. protected Configurable[] buildConfigurables() { List result = Lists.newArrayList(); - myPythonConsoleOptionsPanel = new PyConsoleSpecificOptionsPanel(); - result.add(createConsoleChildConfigurable("Python Console", myPythonConsoleOptionsPanel, + PyConsoleSpecificOptionsPanel pythonConsoleOptionsPanel = new PyConsoleSpecificOptionsPanel(myProject); + result.add(createConsoleChildConfigurable("Python Console", pythonConsoleOptionsPanel, myOptionsProvider.getPythonConsoleSettings(), CONSOLE_SETTINGS_HELP_REFERENCE_PYTHON)); - if (DjangoFacet.isPresentInAnyModule(myProject)) { - myDjangoConsoleOptionsPanel = new PyConsoleSpecificOptionsPanel(); - result.add(createConsoleChildConfigurable("Django Console", - myDjangoConsoleOptionsPanel, myOptionsProvider.getDjangoConsoleSettings(), - CONSOLE_SETTINGS_HELP_REFERENCE_DJANGO)); + for (PyConsoleOptionsProvider provider : Extensions.getExtensions(PyConsoleOptionsProvider.EP_NAME)) { + if (provider.isApplicableTo(myProject)) { + result.add(createConsoleChildConfigurable(provider.getName(), + new PyConsoleSpecificOptionsPanel(myProject), + provider.getSettings(myProject), + provider.getHelpTopic())); + } } - return result.toArray(new Configurable[result.size()]); } - private Configurable createConsoleChildConfigurable(final String name, - final PyConsoleSpecificOptionsPanel panel, - final PyConsoleOptionsProvider.PyConsoleSettings settings, final String helpReference) { + private static Configurable createConsoleChildConfigurable(final String name, + final PyConsoleSpecificOptionsPanel panel, + final PyConsoleOptions.PyConsoleSettings settings, final String helpReference) { return new SearchableConfigurable() { @NotNull @@ -93,7 +91,7 @@ public class PyConsoleOptionsConfigurable extends SearchableConfigurable.Parent. @Override public JComponent createComponent() { - return panel.createPanel(myProject, settings); + return panel.createPanel(settings); } @Override @@ -132,7 +130,7 @@ public class PyConsoleOptionsConfigurable extends SearchableConfigurable.Parent. public JComponent createComponent() { myPanel = new PyConsoleOptionsPanel(); - return myPanel.createPanel(myProject, myOptionsProvider); + return myPanel.createPanel(myOptionsProvider); } @Override @@ -160,9 +158,9 @@ public class PyConsoleOptionsConfigurable extends SearchableConfigurable.Parent. private JPanel myWholePanel; private JBCheckBox myShowDebugConsoleByDefault; private JBCheckBox myShowSeparatorLine; - private PyConsoleOptionsProvider myOptionsProvider; + private PyConsoleOptions myOptionsProvider; - public JPanel createPanel(Project project, PyConsoleOptionsProvider optionsProvider) { + public JPanel createPanel(PyConsoleOptions optionsProvider) { myOptionsProvider = optionsProvider; return myWholePanel; diff --git a/python/src/com/jetbrains/python/console/PyConsoleOptionsProvider.java b/python/src/com/jetbrains/python/console/PyConsoleOptionsProvider.java index 76d39e7410c5..e5ea65bc630d 100644 --- a/python/src/com/jetbrains/python/console/PyConsoleOptionsProvider.java +++ b/python/src/com/jetbrains/python/console/PyConsoleOptionsProvider.java @@ -1,186 +1,16 @@ package com.jetbrains.python.console; -import com.google.common.collect.Maps; -import com.intellij.openapi.components.*; -import com.intellij.openapi.components.StoragePathMacros; -import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.project.Project; -import com.intellij.util.containers.ComparatorUtil; -import com.intellij.util.xmlb.annotations.Transient; -import com.jetbrains.python.run.AbstractPyCommonOptionsForm; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; /** - * @author traff + * @author yole */ -@State( - name = "PyConsoleOptionsProvider", - storages = { - @Storage(file = StoragePathMacros.WORKSPACE_FILE) - } -) -public class PyConsoleOptionsProvider implements PersistentStateComponent { - private State myState = new State(); +public interface PyConsoleOptionsProvider { + ExtensionPointName EP_NAME = ExtensionPointName.create("Pythonid.consoleOptionsProvider"); - @NotNull - private final Project myProject; - - public PyConsoleOptionsProvider(@NotNull Project project) { - myProject = project; - myState.setProject(project); - } - - public PyConsoleSettings getPythonConsoleSettings() { - return myState.myPythonConsoleState; - } - - public PyConsoleSettings getDjangoConsoleSettings() { - return myState.myDjangoConsoleState; - } - - public boolean isShowDebugConsoleByDefault() { - return myState.myShowDebugConsoleByDefault; - } - - public void setShowDebugConsoleByDefault(boolean showDebugConsoleByDefault) { - myState.myShowDebugConsoleByDefault = showDebugConsoleByDefault; - } - - public boolean isShowSeparatorLine() { - return myState.myShowSeparatorLine; - } - - public void setShowSeparatorLine(boolean showSeparatorLine) { - myState.myShowSeparatorLine = showSeparatorLine; - } - - - public static PyConsoleOptionsProvider getInstance(Project project) { - return ServiceManager.getService(project, PyConsoleOptionsProvider.class); - } - - @Override - public State getState() { - return myState; - } - - @Override - public void loadState(State state) { - myState.myShowDebugConsoleByDefault = state.myShowDebugConsoleByDefault; - myState.myShowSeparatorLine = state.myShowSeparatorLine; - myState.myPythonConsoleState = state.myPythonConsoleState; - myState.myDjangoConsoleState = state.myDjangoConsoleState; - myState.setProject(myProject); - } - - public static class State { - public PyConsoleSettings myPythonConsoleState = new PyConsoleSettings(); - public PyConsoleSettings myDjangoConsoleState = new PyConsoleSettings(); - - public boolean myShowDebugConsoleByDefault = false; - public boolean myShowSeparatorLine = true; - - public void setProject(Project project) { - myPythonConsoleState.myProject = project; - myDjangoConsoleState.myProject = project; - } - } - - public static class PyConsoleSettings { - public String myCustomStartScript = ""; - public String mySdkHome = null; - public String myInterpreterOptions = ""; - public boolean myUseModuleSdk; - public String myModuleName = null; - public Map myEnvs = Maps.newHashMap(); - public String myWorkingDirectory = ""; - public boolean myAddContentRoots = true; - public boolean myAddSourceRoots; - - @Transient - private Project myProject; - - public String getCustomStartScript() { - return myCustomStartScript; - } - - public String getSdkHome() { - return mySdkHome; - } - - public void apply(AbstractPyCommonOptionsForm form) { - mySdkHome = form.getSdkHome(); - myInterpreterOptions = form.getInterpreterOptions(); - myEnvs = form.getEnvs(); - myUseModuleSdk = form.isUseModuleSdk(); - myModuleName = form.getModule() == null ? null : form.getModule().getName(); - myWorkingDirectory = form.getWorkingDirectory(); - - myAddContentRoots = form.addContentRoots(); - myAddSourceRoots = form.addSourceRoots(); - } - - public boolean isModified(AbstractPyCommonOptionsForm form) { - return !ComparatorUtil.equalsNullable(mySdkHome, form.getSdkHome()) || - !myInterpreterOptions.equals(form.getInterpreterOptions()) || - !myEnvs.equals(form.getEnvs()) || - myUseModuleSdk != form.isUseModuleSdk() || - myAddContentRoots != form.addContentRoots() || - myAddSourceRoots != form.addSourceRoots() - || !ComparatorUtil.equalsNullable(myModuleName, form.getModule() == null ? null : form.getModule().getName()) - || !myWorkingDirectory.equals(form.getWorkingDirectory()); - } - - public void reset(AbstractPyCommonOptionsForm form) { - form.setEnvs(myEnvs); - form.setInterpreterOptions(myInterpreterOptions); - form.setSdkHome(mySdkHome); - form.setUseModuleSdk(myUseModuleSdk); - form.addContentRoots(myAddContentRoots); - form.addSourceRoots(myAddSourceRoots); - boolean moduleWasAutoselected = false; - if (form.isUseModuleSdk() != myUseModuleSdk) { - myUseModuleSdk = form.isUseModuleSdk(); - moduleWasAutoselected = true; - } - - if (myModuleName != null) { - form.setModule(ModuleManager.getInstance(myProject).findModuleByName(myModuleName)); - } - - if (moduleWasAutoselected && form.getModule() != null) { - myModuleName = form.getModule().getName(); - } - - form.setWorkingDirectory(form.getWorkingDirectory()); - } - - public String getModuleName() { - return myModuleName; - } - - public String getWorkingDirectory() { - return myWorkingDirectory; - } - - public boolean isUseModuleSdk() { - return myUseModuleSdk; - } - - public Map getEnvs() { - return myEnvs; - } - - public boolean addContentRoots() { - return myAddContentRoots; - } - - public boolean addSourceRoots() { - return myAddSourceRoots; - } - - } + boolean isApplicableTo(Project project); + String getName(); + String getHelpTopic(); + PyConsoleOptions.PyConsoleSettings getSettings(Project project); } - diff --git a/python/src/com/jetbrains/python/console/PyConsoleSpecificOptionsPanel.java b/python/src/com/jetbrains/python/console/PyConsoleSpecificOptionsPanel.java index 52cebdca0c67..091c9509bf41 100644 --- a/python/src/com/jetbrains/python/console/PyConsoleSpecificOptionsPanel.java +++ b/python/src/com/jetbrains/python/console/PyConsoleSpecificOptionsPanel.java @@ -7,7 +7,6 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDocumentManager; import com.intellij.ui.EditorTextField; import com.intellij.util.ui.UIUtil; -import com.intellij.xml.actions.xmlbeans.UIUtils; import com.jetbrains.python.PythonFileType; import com.jetbrains.python.psi.impl.PyExpressionCodeFragmentImpl; import com.jetbrains.python.run.AbstractPyCommonOptionsForm; @@ -24,22 +23,27 @@ import java.util.List; * @author traff */ public class PyConsoleSpecificOptionsPanel { + private final Project myProject; private JPanel myWholePanel; private JPanel myStartingScriptPanel; private JPanel myInterpreterPanel; - private PyConsoleOptionsProvider.PyConsoleSettings myConsoleSettings; + private PyConsoleOptions.PyConsoleSettings myConsoleSettings; private EditorTextField myEditorTextField; private AbstractPyCommonOptionsForm myCommonOptionsForm; - public JPanel createPanel(final Project project, final PyConsoleOptionsProvider.PyConsoleSettings optionsProvider) { + public PyConsoleSpecificOptionsPanel(Project project) { + myProject = project; + } + + public JPanel createPanel(final PyConsoleOptions.PyConsoleSettings optionsProvider) { myInterpreterPanel.setLayout(new BorderLayout()); - myCommonOptionsForm = PyCommonOptionsFormFactory.getInstance().createForm(createCommonOptionsFormData(project)); + myCommonOptionsForm = PyCommonOptionsFormFactory.getInstance().createForm(createCommonOptionsFormData()); myCommonOptionsForm.subscribe(); myInterpreterPanel.add(myCommonOptionsForm.getMainPanel(), BorderLayout.CENTER); - configureStartingScriptPanel(project, optionsProvider); + configureStartingScriptPanel(optionsProvider); return myWholePanel; } @@ -61,19 +65,19 @@ public class PyConsoleSpecificOptionsPanel { } }); - myConsoleSettings.reset(myCommonOptionsForm); + myConsoleSettings.reset(myProject, myCommonOptionsForm); } - private static PyCommonOptionsFormData createCommonOptionsFormData(final Project project) { + private PyCommonOptionsFormData createCommonOptionsFormData() { return new PyCommonOptionsFormData() { @Override public Project getProject() { - return project; + return myProject; } @Override public List getValidModules() { - return AbstractPythonRunConfiguration.getValidModules(project); + return AbstractPythonRunConfiguration.getValidModules(myProject); } @Override @@ -83,9 +87,9 @@ public class PyConsoleSpecificOptionsPanel { }; } - private void configureStartingScriptPanel(final Project project, final PyConsoleOptionsProvider.PyConsoleSettings optionsProvider) { + private void configureStartingScriptPanel(final PyConsoleOptions.PyConsoleSettings optionsProvider) { myEditorTextField = - new EditorTextField(createDocument(project, optionsProvider.myCustomStartScript), project, PythonFileType.INSTANCE) { + new EditorTextField(createDocument(myProject, optionsProvider.myCustomStartScript), myProject, PythonFileType.INSTANCE) { @Override protected EditorEx createEditor() { final EditorEx editor = super.createEditor(); diff --git a/python/src/com/jetbrains/python/console/PythonConsoleView.java b/python/src/com/jetbrains/python/console/PythonConsoleView.java index 4edb451c749a..2e15380c52ac 100644 --- a/python/src/com/jetbrains/python/console/PythonConsoleView.java +++ b/python/src/com/jetbrains/python/console/PythonConsoleView.java @@ -247,7 +247,7 @@ public class PythonConsoleView extends LanguageConsoleViewImpl implements PyCode // Mark editor as console one, to prevent autopopup completion getConsoleEditor().putUserData(PythonConsoleAutopopupBlockingHandler.REPL_KEY, new Object()); - setShowSeparatorLine(PyConsoleOptionsProvider.getInstance(project).isShowSeparatorLine()); + setShowSeparatorLine(PyConsoleOptions.getInstance(project).isShowSeparatorLine()); initComponents(); } diff --git a/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java b/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java index 3cf2c5ed4a94..fd8b39e861cf 100644 --- a/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java +++ b/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java @@ -55,7 +55,7 @@ public class PythonDebugLanguageConsoleView extends JPanel implements ConsoleVie add(myTextConsole.getComponent(), TEXT_CONSOLE_PANEL); add(myPydevConsoleView.getComponent(), PYDEV_CONSOLE_PANEL); - showDebugConsole(PyConsoleOptionsProvider.getInstance(project).isShowDebugConsoleByDefault()); + showDebugConsole(PyConsoleOptions.getInstance(project).isShowDebugConsoleByDefault()); Disposer.register(this, myPydevConsoleView); Disposer.register(this, myTextConsole); diff --git a/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java b/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java index 8475a98bcbe7..9c0f82915e1e 100644 --- a/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java +++ b/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java @@ -20,7 +20,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.PathMappingSettings; import com.jetbrains.python.buildout.BuildoutFacet; -import com.jetbrains.python.remote.PyRemoteSdkAdditionalData; +import com.jetbrains.python.remote.PyRemoteSdkData; import com.jetbrains.python.remote.PythonRemoteInterpreterManager; import com.jetbrains.python.run.PythonCommandLineState; import com.jetbrains.python.sdk.PySdkUtil; @@ -76,7 +76,7 @@ public class RunPythonConsoleAction extends AnAction implements DumbAware { String[] setupFragment; - PyConsoleOptionsProvider.PyConsoleSettings settingsProvider = PyConsoleOptionsProvider.getInstance(project).getPythonConsoleSettings(); + PyConsoleOptions.PyConsoleSettings settingsProvider = PyConsoleOptions.getInstance(project).getPythonConsoleSettings(); Collection pythonPath = PythonCommandLineState.collectPythonPath(module, settingsProvider.addContentRoots(), settingsProvider.addSourceRoots()); @@ -138,7 +138,7 @@ public class RunPythonConsoleAction extends AnAction implements DumbAware { PythonRemoteInterpreterManager instance = PythonRemoteInterpreterManager.getInstance(); if (instance != null) { mappingSettings = - instance.setupMappings(project, (PyRemoteSdkAdditionalData)sdk.getSdkAdditionalData(), null); + instance.setupMappings(project, (PyRemoteSdkData)sdk.getSdkAdditionalData(), null); } } return mappingSettings; @@ -148,7 +148,7 @@ public class RunPythonConsoleAction extends AnAction implements DumbAware { private static Pair findPythonSdkAndModule(Project project, Module contextModule) { Sdk sdk = null; Module module = null; - PyConsoleOptionsProvider.PyConsoleSettings settings = PyConsoleOptionsProvider.getInstance(project).getPythonConsoleSettings(); + PyConsoleOptions.PyConsoleSettings settings = PyConsoleOptions.getInstance(project).getPythonConsoleSettings(); String sdkHome = settings.getSdkHome(); if (sdkHome != null) { sdk = PythonSdkType.findSdkByPath(sdkHome); diff --git a/python/src/com/jetbrains/python/console/parsing/PyConsoleParsingContext.java b/python/src/com/jetbrains/python/console/parsing/PyConsoleParsingContext.java index bd2ffb22d7e4..ba6eaae70527 100644 --- a/python/src/com/jetbrains/python/console/parsing/PyConsoleParsingContext.java +++ b/python/src/com/jetbrains/python/console/parsing/PyConsoleParsingContext.java @@ -138,7 +138,7 @@ public class PyConsoleParsingContext extends ParsingContext { if (myBuilder.getTokenType() == PyTokenTypes.IDENTIFIER) { myBuilder.advanceLexer(); - command.done(PyElementTypes.REFERENCE_EXPRESSION); + command.done(getReferenceType()); } else { expr.drop(); diff --git a/python/src/com/jetbrains/python/debugger/PyBreakpointType.java b/python/src/com/jetbrains/python/debugger/PyBreakpointType.java new file mode 100644 index 000000000000..abe67e979561 --- /dev/null +++ b/python/src/com/jetbrains/python/debugger/PyBreakpointType.java @@ -0,0 +1,11 @@ +package com.jetbrains.python.debugger; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; + +/** + * @author yole + */ +public interface PyBreakpointType { + boolean canPutInDocument(Project project, Document document); +} diff --git a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java index ac414246fa4e..5b1b08ed9780 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java +++ b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java @@ -23,15 +23,14 @@ import com.intellij.remotesdk.RemoteProcessHandlerBase; import com.intellij.xdebugger.*; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.breakpoints.XBreakpointHandler; +import com.intellij.xdebugger.breakpoints.XBreakpointType; import com.intellij.xdebugger.breakpoints.XLineBreakpoint; import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.frame.XValueChildrenList; import com.intellij.xdebugger.stepping.XSmartStepIntoHandler; -import com.jetbrains.django.util.DjangoUtil; import com.jetbrains.python.console.pydev.PydevCompletionVariant; -import com.jetbrains.python.debugger.django.DjangoTemplateLineBreakpointType; import com.jetbrains.python.debugger.pydev.*; -import com.jetbrains.python.debugger.remote.vfs.PyRemotePositionConverter; +import com.jetbrains.python.remote.RemoteDebuggableProcessHandler; import com.jetbrains.python.run.PythonProcessHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -99,8 +98,8 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr if (myProcessHandler != null) { myProcessHandler.addProcessListener(this); } - if (processHandler instanceof RemoteProcessHandlerBase) { - myPositionConverter = new PyRemotePositionConverter(this, ((RemoteProcessHandlerBase)processHandler).getMappingSettings()); + if (processHandler instanceof RemoteDebuggableProcessHandler) { + myPositionConverter = ((RemoteDebuggableProcessHandler) processHandler).createPositionConverter(this); } else { myPositionConverter = new PyLocalPositionConverter(); @@ -264,12 +263,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr @Override public void recordSignature(PySignature signature) { - if (myPositionConverter instanceof PyRemotePositionConverter) { - String localPath = ((PyRemotePositionConverter)myPositionConverter).getPathMappingSettings().convertToLocal(signature.getFile()); - signature = new PySignature(localPath, signature.getFunctionName()).addAllArgs(signature); - } - - PySignatureCacheManager.getInstance(getSession().getProject()).recordSignature(signature); + PySignatureCacheManager.getInstance(getSession().getProject()).recordSignature(myPositionConverter.convertSignature(signature)); } protected void afterConnect() { @@ -425,8 +419,12 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr String type = PyLineBreakpointType.ID; final Document document = FileDocumentManager.getInstance().getDocument(position.getFile()); if (document != null) { - if (DjangoUtil.isDjangoTemplateDocument(document, getSession().getProject())) { - type = DjangoTemplateLineBreakpointType.ID; + for (XBreakpointType breakpointType : Extensions.getExtensions(XBreakpointType.EXTENSION_POINT_NAME)) { + if (breakpointType instanceof PyBreakpointType && + ((PyBreakpointType)breakpointType).canPutInDocument(getSession().getProject(), document)) { + type = breakpointType.getId(); + break; + } } } myDebugger.setTempBreakpoint(type, pyPosition.getFile(), pyPosition.getLine()); diff --git a/python/src/com/jetbrains/python/debugger/PyLocalPositionConverter.java b/python/src/com/jetbrains/python/debugger/PyLocalPositionConverter.java index 509fdf25b20b..c725730de10f 100644 --- a/python/src/com/jetbrains/python/debugger/PyLocalPositionConverter.java +++ b/python/src/com/jetbrains/python/debugger/PyLocalPositionConverter.java @@ -89,6 +89,11 @@ public class PyLocalPositionConverter implements PyPositionConverter { return createXSourcePosition(getVirtualFile(position.getFile()), position.getLine()); } + @Override + public PySignature convertSignature(PySignature signature) { + return signature; + } + public VirtualFile getVirtualFile(String path) { VirtualFile vFile = getLocalFileSystem().findFileByPath(path); diff --git a/python/src/com/jetbrains/python/facet/PythonPathContributingFacet.java b/python/src/com/jetbrains/python/facet/PythonPathContributingFacet.java index 7bba6c11715b..2e97f4489b56 100644 --- a/python/src/com/jetbrains/python/facet/PythonPathContributingFacet.java +++ b/python/src/com/jetbrains/python/facet/PythonPathContributingFacet.java @@ -7,4 +7,5 @@ import java.util.List; */ public interface PythonPathContributingFacet { List getAdditionalPythonPath(); + boolean acceptRootAsTopLevelPackage(); } diff --git a/python/src/com/jetbrains/python/inspections/PyDocstringInspection.java b/python/src/com/jetbrains/python/inspections/PyDocstringInspection.java index 5cd4b5a4aa82..0ed899afdbaa 100644 --- a/python/src/com/jetbrains/python/inspections/PyDocstringInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyDocstringInspection.java @@ -8,16 +8,15 @@ import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.codeInspection.SuppressIntentionAction; import com.intellij.lang.ASTNode; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.util.PsiTreeUtil; -import com.jetbrains.django.model.DjangoMeta; -import com.jetbrains.django.model.DjangoModel; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; -import com.jetbrains.python.documentation.*; +import com.jetbrains.python.documentation.DocStringUtil; import com.jetbrains.python.inspections.quickfix.DocstringQuickFix; import com.jetbrains.python.inspections.quickfix.PySuppressInspectionFix; import com.jetbrains.python.psi.*; @@ -76,9 +75,15 @@ public class PyDocstringInspection extends PyInspection { public void visitPyClass(PyClass node) { if (PythonUnitTestUtil.isUnitTestCaseClass(node)) return; final String name = node.getName(); - final PyClass outerClass = PsiTreeUtil.getParentOfType(node, PyClass.class); - final boolean isDjangoMeta = DjangoModel.isDjangoModelDescendant(outerClass) && DjangoMeta.isMetaClass(node); - if (name != null && !name.startsWith("_") && !isDjangoMeta) checkDocString(node); + if (name == null || name.startsWith("_")) { + return; + } + for (PyInspectionExtension extension : Extensions.getExtensions(PyInspectionExtension.EP_NAME)) { + if (extension.ignoreMissingDocstring(node)) { + return; + } + } + checkDocString(node); } private void checkDocString(PyDocStringOwner node) { diff --git a/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java b/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java index 601f3ee1d592..e2d5989bdcdf 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java +++ b/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java @@ -211,7 +211,7 @@ public class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor { } void registerProblems() { - final UnusedLocalFilter[] filters = Extensions.getExtensions(UnusedLocalFilter.EP_NAME); + final PyInspectionExtension[] filters = Extensions.getExtensions(PyInspectionExtension.EP_NAME); // Register problems final Set functionsWithInheritors = new HashSet(); @@ -219,7 +219,7 @@ public class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor { for (PsiElement element : myUnusedElements) { boolean ignoreUnused = false; - for (UnusedLocalFilter filter : filters) { + for (PyInspectionExtension filter : filters) { if (filter.ignoreUnused(element)) { ignoreUnused = true; } diff --git a/python/src/com/jetbrains/python/inspections/UnusedLocalFilter.java b/python/src/com/jetbrains/python/inspections/UnusedLocalFilter.java deleted file mode 100644 index e977fac541d7..000000000000 --- a/python/src/com/jetbrains/python/inspections/UnusedLocalFilter.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.jetbrains.python.inspections; - -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.psi.PsiElement; - -/** - * @author yole - */ -public interface UnusedLocalFilter { - ExtensionPointName EP_NAME = ExtensionPointName.create("Pythonid.unusedLocalFilter"); - - boolean ignoreUnused(PsiElement local); -} diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddFunctionQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddFunctionQuickFix.java index cc67a715a211..bd588ba40e4d 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddFunctionQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddFunctionQuickFix.java @@ -6,18 +6,20 @@ import com.intellij.codeInsight.template.TemplateBuilder; import com.intellij.codeInsight.template.TemplateBuilderFactory; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; import com.intellij.util.IncorrectOperationException; -import com.jetbrains.django.facet.DjangoFacet; import com.jetbrains.python.PyBundle; +import com.jetbrains.python.inspections.PyInspectionExtension; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.ParamHelper; import com.jetbrains.python.psi.impl.PyFunctionBuilder; import org.jetbrains.annotations.NotNull; +import java.util.List; + import static com.jetbrains.python.psi.PyUtil.sure; /** @@ -29,10 +31,10 @@ import static com.jetbrains.python.psi.PyUtil.sure; */ public class AddFunctionQuickFix implements LocalQuickFix { - private String myIdentifier; + private final String myIdentifier; private PyFile myPyFile; - public AddFunctionQuickFix(String identifier, PyFile module) { + public AddFunctionQuickFix(@NotNull String identifier, PyFile module) { myIdentifier = identifier; myPyFile = module; } @@ -52,15 +54,14 @@ public class AddFunctionQuickFix implements LocalQuickFix { // descriptor points to the unresolved identifier // there can be no name clash, else the name would have resolved, and it hasn't. PsiElement problem_elt = descriptor.getPsiElement().getParent(); // id -> ref expr - String item_name = myIdentifier; - sure(myPyFile); sure(item_name); + sure(myPyFile); sure(FileModificationService.getInstance().preparePsiElementForWrite(myPyFile)); // try to at least match parameter count // TODO: get parameter style from code style - PyFunctionBuilder builder = new PyFunctionBuilder(item_name); - PsiElement problem_parent = problem_elt.getParent(); - if (problem_parent instanceof PyCallExpression) { - PyArgumentList arglist = ((PyCallExpression)problem_parent).getArgumentList(); + PyFunctionBuilder builder = new PyFunctionBuilder(myIdentifier); + PsiElement problemParent = problem_elt.getParent(); + if (problemParent instanceof PyCallExpression) { + PyArgumentList arglist = ((PyCallExpression)problemParent).getArgumentList(); sure(arglist); final PyExpression[] args = arglist.getArguments(); for (PyExpression arg : args) { @@ -76,10 +77,15 @@ public class AddFunctionQuickFix implements LocalQuickFix { } } } - else if (problem_parent != null) { - PsiFile source_file = problem_parent.getContainingFile(); - if (source_file != null && "urls.py".equals(source_file.getName()) && DjangoFacet.isPresent(source_file)) { - builder.parameter("request"); // specifically for mentions in urlpatterns + else if (problemParent != null) { + for (PyInspectionExtension extension : Extensions.getExtensions(PyInspectionExtension.EP_NAME)) { + List params = extension.getFunctionParametersFromUsage(problem_elt); + if (params != null) { + for (String param : params) { + builder.parameter(param); + } + break; + } } } // else: no arglist, use empty args diff --git a/python/src/com/jetbrains/python/lexer/PythonIndentingProcessor.java b/python/src/com/jetbrains/python/lexer/PythonIndentingProcessor.java index 4ad93f840490..2cac358435f3 100644 --- a/python/src/com/jetbrains/python/lexer/PythonIndentingProcessor.java +++ b/python/src/com/jetbrains/python/lexer/PythonIndentingProcessor.java @@ -5,8 +5,8 @@ import com.intellij.lexer.FlexLexer; import com.intellij.lexer.MergingLexerAdapter; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; -import com.jetbrains.cython.parser.CythonTokenTypes; import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.PythonDialectsTokenSetProvider; import gnu.trove.TIntStack; import org.jetbrains.annotations.Nullable; @@ -21,13 +21,7 @@ public class PythonIndentingProcessor extends MergingLexerAdapter { private int myCurrentNewLineIndent = 0; private static final boolean DUMP_TOKENS = false; - private static final TokenSet RECOVERY_TOKENS = - TokenSet.create(PyTokenTypes.DEF_KEYWORD, PyTokenTypes.CLASS_KEYWORD, PyTokenTypes.RETURN_KEYWORD, PyTokenTypes.WITH_KEYWORD, - PyTokenTypes.WHILE_KEYWORD, PyTokenTypes.BREAK_KEYWORD, PyTokenTypes.CONTINUE_KEYWORD, PyTokenTypes.RAISE_KEYWORD, - PyTokenTypes.TRY_KEYWORD, PyTokenTypes.EXCEPT_KEYWORD, PyTokenTypes.FINALLY_KEYWORD, - // TODO: Cython dependency. Collect these tokens via the PythonDialectsTokenSetProvider (requires setting up the - // "Pythonid.dialectsTokenSetContributor" extension point in all the tests. - CythonTokenTypes.CDEF_KEYWORD, CythonTokenTypes.CPDEF_KEYWORD, CythonTokenTypes.CTYPEDEF_KEYWORD); + private final TokenSet RECOVERY_TOKENS = PythonDialectsTokenSetProvider.INSTANCE.getUnbalancedBracesRecoveryTokens(); public PythonIndentingProcessor(FlexLexer lexer, TokenSet tokens) { super(new FlexAdapter(lexer), tokens); diff --git a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java index 05bee8401122..efa10ccd8128 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java @@ -7,6 +7,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.PythonDialectsTokenSetProvider; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.PyResolveUtil; @@ -47,7 +48,7 @@ public class PyDecoratorImpl extends StubBasedPsiElementBase im } public boolean isBuiltin() { - ASTNode node = getNode().findChildByType(PyElementTypes.REFERENCE_EXPRESSION); + ASTNode node = getNode().findChildByType(PythonDialectsTokenSetProvider.INSTANCE.getReferenceExpressionTokens()); if (node != null) { PyReferenceExpression ref = (PyReferenceExpression)node.getPsi(); PsiElement target = ref.getReference().resolve(); diff --git a/python/src/com/jetbrains/python/psi/impl/PyFromImportStatementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFromImportStatementImpl.java index f887dece66af..b43511d0d442 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFromImportStatementImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFromImportStatementImpl.java @@ -4,11 +4,11 @@ import com.intellij.lang.ASTNode; import com.intellij.psi.*; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.stubs.IStubElementType; -import com.intellij.psi.tree.TokenSet; import com.intellij.util.ArrayFactory; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.PythonDialectsTokenSetProvider; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.resolve.ResolveImportUtil; import com.jetbrains.python.psi.stubs.PyFromImportStatementStub; @@ -50,7 +50,7 @@ public class PyFromImportStatementImpl extends PyBaseElementImpl @Nullable public PyReferenceExpression getImportReferenceExpression() { - final ASTNode node = getNode().findChildByType(PyElementTypes.REFERENCE_EXPRESSION); + final ASTNode node = getNode().findChildByType(PythonDialectsTokenSetProvider.INSTANCE.getReferenceExpressionTokens()); return node == null ? null : (PyReferenceExpression) node.getPsi(); } diff --git a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java index f9f175265e56..05cd3ccc91db 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java @@ -12,7 +12,6 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.PlatformIcons; import com.intellij.util.Processor; -import com.jetbrains.cython.psi.CythonClass; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; @@ -310,9 +309,6 @@ public class PyNamedParameterImpl extends PyPresentableElementImpl { +public abstract class PythonRemoteInterpreterManager { public final static ExtensionPointName EP_NAME = ExtensionPointName.create("Pythonid.remoteInterpreterManager"); public static final String WEB_DEPLOYMENT_PLUGIN_IS_DISABLED = "Remote interpreter can't be executed. Please enable the Remote Hosts Access plugin."; public abstract ProcessHandler startRemoteProcess(@Nullable Project project, - @NotNull PyRemoteSdkAdditionalData data, + @NotNull PyRemoteSdkData data, @NotNull GeneralCommandLine commandLine, @Nullable PathMappingSettings mappingSettings) throws RemoteInterpreterException; public abstract ProcessHandler startRemoteProcessWithPid(@Nullable Project project, - @NotNull PyRemoteSdkAdditionalData data, + @NotNull PyRemoteSdkData data, @NotNull GeneralCommandLine commandLine, @Nullable PathMappingSettings mappingSettings) @@ -120,9 +120,11 @@ public abstract class PythonRemoteInterpreterManager implements RemoteSdkFactory } public abstract PathMappingSettings setupMappings(@Nullable Project project, - @NotNull PyRemoteSdkAdditionalData data, + @NotNull PyRemoteSdkData data, @Nullable PathMappingSettings mappingSettings); + public abstract SdkAdditionalData loadRemoteSdkData(Sdk sdk, Element additional); + public static class PyRemoteInterpreterExecutionException extends ExecutionException { public PyRemoteInterpreterExecutionException() { diff --git a/python/src/com/jetbrains/python/remote/RemoteDebuggableProcessHandler.java b/python/src/com/jetbrains/python/remote/RemoteDebuggableProcessHandler.java new file mode 100644 index 000000000000..57a89dd62db9 --- /dev/null +++ b/python/src/com/jetbrains/python/remote/RemoteDebuggableProcessHandler.java @@ -0,0 +1,12 @@ +package com.jetbrains.python.remote; + +import com.intellij.remotesdk.RemoteProcessHandlerBase; +import com.jetbrains.python.debugger.PyDebugProcess; +import com.jetbrains.python.debugger.PyPositionConverter; + +/** + * @author yole + */ +public interface RemoteDebuggableProcessHandler extends RemoteProcessHandlerBase { + PyPositionConverter createPositionConverter(PyDebugProcess debugProcess); +} diff --git a/python/src/com/jetbrains/python/remote/RemoteProjectSettings.java b/python/src/com/jetbrains/python/remote/RemoteProjectSettings.java new file mode 100644 index 000000000000..c2fc5a4b0ccf --- /dev/null +++ b/python/src/com/jetbrains/python/remote/RemoteProjectSettings.java @@ -0,0 +1,23 @@ +package com.jetbrains.python.remote; + +/** +* @author traff +*/ +public class RemoteProjectSettings { + private String myDeploymentName; + private String myRemoteRoot; + + + public RemoteProjectSettings(String deploymentName, String remoteRoot) { + myDeploymentName = deploymentName; + myRemoteRoot = remoteRoot; + } + + public String getDeploymentName() { + return myDeploymentName; + } + + public String getRemoteRoot() { + return myRemoteRoot; + } +} diff --git a/python/src/com/jetbrains/python/run/PyRemoteProcessStarter.java b/python/src/com/jetbrains/python/run/PyRemoteProcessStarter.java index 3fd92bedd43c..ceccd1e5f79b 100644 --- a/python/src/com/jetbrains/python/run/PyRemoteProcessStarter.java +++ b/python/src/com/jetbrains/python/run/PyRemoteProcessStarter.java @@ -10,7 +10,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.ui.Messages; import com.intellij.util.PathMappingSettings; -import com.jetbrains.python.remote.PyRemoteSdkAdditionalData; +import com.jetbrains.python.remote.PyRemoteSdkData; import com.jetbrains.python.remote.PythonRemoteInterpreterManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -59,7 +59,7 @@ public class PyRemoteProcessStarter { @Nullable PathMappingSettings settings) throws ExecutionException { - return manager.startRemoteProcess(project, (PyRemoteSdkAdditionalData)sdk.getSdkAdditionalData(), commandLine, + return manager.startRemoteProcess(project, (PyRemoteSdkData)sdk.getSdkAdditionalData(), commandLine, settings); } } diff --git a/python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java b/python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java index fdd356a85a04..7be161979cd2 100644 --- a/python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java +++ b/python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java @@ -72,7 +72,9 @@ public class InterpreterPathChooser extends BaseListPopupStep { paths.add(FileUtil.getLocationRelativeToUserHome(sdkHome)); } paths.add(LOCAL); - paths.add(REMOTE); + if (PythonRemoteInterpreterManager.getInstance() != null) { + paths.add(REMOTE); + } if (showVirtualEnv) { paths.add(VIRTUALENV); } diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index 60dca0173455..8f266af4d084 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -51,7 +51,7 @@ import com.jetbrains.python.facet.PythonFacetSettings; import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.search.PyProjectScopeBuilder; -import com.jetbrains.python.remote.PyRemoteSdkAdditionalData; +import com.jetbrains.python.remote.PythonRemoteInterpreterManager; import com.jetbrains.python.sdk.flavors.CPythonSdkFlavor; import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; import com.jetbrains.python.sdk.skeletons.PySkeletonRefresher; @@ -390,11 +390,12 @@ public class PythonSdkType extends SdkType { @Override public SdkAdditionalData loadAdditionalData(final Sdk currentSdk, final Element additional) { if (RemoteSdkDataHolder.isRemoteSdk(currentSdk.getHomePath())) { - return PyRemoteSdkAdditionalData.loadRemote(currentSdk, additional); - } - else { - return PythonSdkAdditionalData.load(currentSdk, additional); + PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance(); + if (manager != null) { + return manager.loadRemoteSdkData(currentSdk, additional); + } } + return PythonSdkAdditionalData.load(currentSdk, additional); } private boolean switchPathToInterpreter(Sdk currentSdk, String... variants) { diff --git a/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java b/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java index d9cb855f6ac9..1cab27895f8a 100644 --- a/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java +++ b/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java @@ -25,6 +25,7 @@ public class PythonAllTestsSuite { public static final Class[] tests = { PythonLexerTest.class, PyStringLiteralLexerTest.class, + CythonLexerTest.class, PyStringFormatParserTest.class, PyEncodingTest.class, PythonParsingTest.class, diff --git a/python/testSrc/com/jetbrains/python/PythonParsingTest.java b/python/testSrc/com/jetbrains/python/PythonParsingTest.java index 53687856cf7a..75da8f3d51c5 100644 --- a/python/testSrc/com/jetbrains/python/PythonParsingTest.java +++ b/python/testSrc/com/jetbrains/python/PythonParsingTest.java @@ -16,12 +16,12 @@ public class PythonParsingTest extends ParsingTestCase { public PythonParsingTest() { super("psi", "py", new PythonParserDefinition()); - PyTestCase.initPlatformPrefix(); } @Override protected void setUp() throws Exception { super.setUp(); + PyTestCase.initPlatformPrefix(); registerExtensionPoint(PythonDialectsTokenSetContributor.EP_NAME, PythonDialectsTokenSetContributor.class); registerExtension(PythonDialectsTokenSetContributor.EP_NAME, new PythonTokenSetContributor()); registerExtension(PythonDialectsTokenSetContributor.EP_NAME, new CythonTokenSetContributor()); diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyLexerTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyLexerTestCase.java index 9e3a0f16e01d..40ddfaf84fc3 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyLexerTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyLexerTestCase.java @@ -1,12 +1,24 @@ package com.jetbrains.python.fixtures; import com.intellij.lexer.Lexer; -import junit.framework.TestCase; +import com.intellij.testFramework.PlatformLiteFixture; +import com.jetbrains.cython.CythonTokenSetContributor; +import com.jetbrains.python.PythonDialectsTokenSetContributor; +import com.jetbrains.python.PythonTokenSetContributor; /** * @author yole */ -public abstract class PyLexerTestCase extends TestCase { +public abstract class PyLexerTestCase extends PlatformLiteFixture { + @Override + protected void setUp() throws Exception { + super.setUp(); + PyTestCase.initPlatformPrefix(); + registerExtensionPoint(PythonDialectsTokenSetContributor.EP_NAME, PythonDialectsTokenSetContributor.class); + registerExtension(PythonDialectsTokenSetContributor.EP_NAME, new PythonTokenSetContributor()); + registerExtension(PythonDialectsTokenSetContributor.EP_NAME, new CythonTokenSetContributor()); + } + public static void doLexerTest(String text, Lexer lexer, String... expectedTokens) { lexer.start(text); int idx = 0;