Merge remote branch 'origin/master'

This commit is contained in:
Dmitry Jemerov
2011-11-24 21:48:24 +01:00
15 changed files with 136 additions and 28 deletions
@@ -694,6 +694,10 @@
<lang.elementManipulator forClass="com.jetbrains.mako.psi.impl.MakoTagAttribute"
implementationClass="com.jetbrains.mako.psi.MakoTagAttributeManipulator"/>
<localInspection language="Python" shortName="MakoArgumentListInspection" displayName="Incorrect call arguments"
groupKey="INSP.GROUP.mako" enabledByDefault="true" level="WARNING"
implementationClass="com.jetbrains.mako.inspection.MakoArgumentListInspection"/>
<moduleService serviceInterface="com.jetbrains.python.templateLanguages.TemplatesService"
serviceImplementation="com.jetbrains.python.templateLanguages.TemplatesService"/>
@@ -194,6 +194,7 @@ surround.with.try.except.template=try / except
### Inspections: INSP ###
INSP.GROUP.python=Python
INSP.GROUP.mako=Mako
# PyArgumentListInspection
INSP.NAME.incorrect.call.arguments=Incorrect call arguments
@@ -600,6 +601,8 @@ sdk.error.invalid.interpreter.name.$0=Invalid Python interpeter name '{0}'!
sdk.select.path=Select Python Interpreter
runcfg.unittest.dlg.pattern=Pattern:
runcfg.testing.no.test.framework=No {0} runner found in selected interpreter
# Consoles messages
python.console=Python Console
django.console=Django Console
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.configuration.PyIntegratedToolsConfigurable">
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="5" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="6" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="657" height="302"/>
@@ -56,7 +56,7 @@
</component>
<vspacer id="fc19e">
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="40acf" class="com.intellij.ui.components.JBLabel">
@@ -76,6 +76,14 @@
<text value=""/>
</properties>
</component>
<grid id="728f8" binding="myErrorPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<grid row="4" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
</children>
</grid>
</form>
@@ -1,39 +1,34 @@
package com.jetbrains.python.configuration;
import com.google.common.collect.Lists;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
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.editor.impl.EditorFactoryImpl;
import com.intellij.facet.impl.ui.FacetErrorPanel;
import com.intellij.facet.ui.FacetEditorValidator;
import com.intellij.facet.ui.ValidationResult;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.NonDefaultProjectConfigurable;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentIterator;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.CollectionComboBoxModel;
import com.intellij.util.FileContentUtil;
import com.jetbrains.django.lang.template.DjangoTemplateFileType;
import com.jetbrains.python.templateLanguages.TemplatesConfigurationsModel;
import com.jetbrains.python.templateLanguages.TemplatesService;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.documentation.DocStringFormat;
import com.jetbrains.python.documentation.PyDocumentationSettings;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.testing.PythonTestConfigurationsModel;
import com.jetbrains.python.testing.TestRunnerService;
import com.jetbrains.python.testing.VFSTestFrameworkListener;
import com.jetbrains.rest.ReSTService;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.util.List;
/**
@@ -48,6 +43,7 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable, No
private final PyDocumentationSettings myDocumentationSettings;
private TextFieldWithBrowseButton myWorkDir;
private JCheckBox txtIsRst;
private JPanel myErrorPanel;
public PyIntegratedToolsConfigurable(Project project) {
myProject = project;
@@ -59,8 +55,41 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable, No
ReSTService service = ReSTService.getInstance(myProject);
myWorkDir.setText(service.getWorkdir());
txtIsRst.setSelected(service.txtIsRst());
initErrorValidation();
}
private void initErrorValidation() {
FacetErrorPanel facetErrorPanel = new FacetErrorPanel();
myErrorPanel.add(facetErrorPanel.getComponent(), BorderLayout.CENTER);
facetErrorPanel.getValidatorsManager().registerValidator(new FacetEditorValidator() {
@Override
public ValidationResult check() {
Module[] modules = ModuleManager.getInstance(myProject).getModules();
if (modules.length == 0) return ValidationResult.OK;
final Sdk sdk = PythonSdkType.findPythonSdk(modules[0]);
if (sdk != null) {
if (myTestRunnerComboBox.getSelectedItem() == PythonTestConfigurationsModel.PY_TEST_NAME) {
if (!VFSTestFrameworkListener.getInstance().isPyTestInstalled(sdk.getHomePath()))
return new ValidationResult(PyBundle.message("runcfg.testing.no.test.framework", "py.test"));
}
else if (myTestRunnerComboBox.getSelectedItem() == PythonTestConfigurationsModel.PYTHONS_NOSETEST_NAME) {
if (!VFSTestFrameworkListener.getInstance().isNoseTestInstalled(sdk.getHomePath()))
return new ValidationResult(PyBundle.message("runcfg.testing.no.test.framework", "nosetest"));
}
else if (myTestRunnerComboBox.getSelectedItem() == PythonTestConfigurationsModel.PYTHONS_ATTEST_NAME) {
if (!VFSTestFrameworkListener.getInstance().isAtTestInstalled(sdk.getHomePath()))
return new ValidationResult(PyBundle.message("runcfg.testing.no.test.framework", "attest"));
}
}
return ValidationResult.OK;
}
}, myTestRunnerComboBox);
facetErrorPanel.getValidatorsManager().validate();
}
@Nls
@Override
public String getDisplayName() {
@@ -6,6 +6,7 @@ import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.cython.CythonLanguageDialect;
import com.jetbrains.mako.MakoLanguage;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
@@ -43,7 +44,7 @@ public class PyArgumentListInspection extends PyInspection {
@Override
public void visitPyArgumentList(final PyArgumentList node) {
if (CythonLanguageDialect._isDisabledFor(node)) {
if (CythonLanguageDialect._isDisabledFor(node) || MakoLanguage._isDisabledFor(node)) {
return;
}
// analyze
@@ -87,14 +88,18 @@ public class PyArgumentListInspection extends PyInspection {
}
public static void inspectPyArgumentList(PyArgumentList node, ProblemsHolder holder, final TypeEvalContext context) {
public static void inspectPyArgumentList(PyArgumentList node, ProblemsHolder holder, final TypeEvalContext context, int implicitOffset) {
if (node.getParent() instanceof PyClass) return; // class Foo(object) is also an arg list
CallArgumentsMapping result = node.analyzeCall(PyResolveContext.noImplicits().withTypeEvalContext(context));
CallArgumentsMapping result = node.analyzeCall(PyResolveContext.noImplicits().withTypeEvalContext(context), implicitOffset);
highlightIncorrectArguments(holder, result);
highlightMissingArguments(node, holder, result);
highlightStarArgumentTypeMismatch(node, holder, context);
}
public static void inspectPyArgumentList(PyArgumentList node, ProblemsHolder holder, final TypeEvalContext context) {
inspectPyArgumentList(node, holder, context, 0);
}
private static void highlightIncorrectArguments(ProblemsHolder holder, CallArgumentsMapping result) {
for (Map.Entry<PyExpression, EnumSet<CallArgumentsMapping.ArgFlag>> arg_entry : result.getArgumentFlags().entrySet()) {
EnumSet<CallArgumentsMapping.ArgFlag> flags = arg_entry.getValue();
@@ -6,9 +6,13 @@ import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter;
import com.intellij.openapi.editor.highlighter.HighlighterClient;
import com.intellij.openapi.fileTypes.SyntaxHighlighter;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PythonFileType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -25,6 +29,9 @@ public class PythonEditorHighlighter extends LexerEditorHighlighter {
scheme);
}
private Boolean hadUnicodeImport = false;
public static final Key<Boolean> KEY = new Key<Boolean>("python.future.import");
@Override
public void documentChanged(DocumentEvent e) {
synchronized (this) {
@@ -34,10 +41,14 @@ public class PythonEditorHighlighter extends LexerEditorHighlighter {
// we should update the whole document
if (l instanceof LayeredLexer) {
Lexer delegate = ((LayeredLexer)l).getDelegate();
int offset = e.getOffset();
int lineNumber = document.getLineNumber(offset);
TextRange tr = new TextRange(document.getLineStartOffset(lineNumber), document.getLineEndOffset(lineNumber));
document.putUserData(KEY, document.getText(tr).indexOf(PyNames.UNICODE_LITERALS) == -1);
Boolean hasUnicodeImport = document.getUserData(KEY);
if (delegate instanceof PythonHighlightingLexer &&
(((PythonHighlightingLexer)delegate).getImportOffset() > e.getOffset()
|| ((PythonHighlightingLexer)delegate).getImportOffset() == -1)) {
|| hasUnicodeImport != hadUnicodeImport)) {
((PythonHighlightingLexer)delegate).clearState(e.getDocument().getTextLength());
setText(document.getCharsSequence());
}
@@ -46,4 +57,19 @@ public class PythonEditorHighlighter extends LexerEditorHighlighter {
else super.documentChanged(e);
}
}
@Override
public void beforeDocumentChange(DocumentEvent e) {
final Document document = e.getDocument();
hadUnicodeImport = document.getUserData(KEY);
}
@Override
public void setEditor(HighlighterClient editor) {
Lexer l = getLexer();
if (l instanceof LayeredLexer) {
editor.getDocument().putUserData(KEY, editor.getDocument().getText().indexOf(PyNames.UNICODE_LITERALS) == -1);
}
super.setEditor(editor);
}
}
@@ -30,10 +30,15 @@ public interface PyArgumentList extends PyElement {
* Tries to map the argument list to callee's idea of parameters.
* @return a result object with mappings and diagnostic flags.
* @param resolveContext the reference resolution context
* @param implicitOffset known from the context implicit offset
*/
@NotNull
CallArgumentsMapping analyzeCall(PyResolveContext resolveContext, int implicitOffset);
@NotNull
CallArgumentsMapping analyzeCall(PyResolveContext resolveContext);
@Nullable
ASTNode getClosingParen();
}
@@ -55,6 +55,14 @@ public interface PyCallExpression extends PyExpression {
@Nullable
PyMarkedCallee resolveCallee(PyResolveContext resolveContext);
/**
*
* @param resolveContext the reference resolve context
* @param implicitOffset known from the context implicit offset
*/
@Nullable
PyMarkedCallee resolveCallee(PyResolveContext resolveContext, int implicitOffset);
/**
* Checks if the unqualified name of the callee matches any of the specified names
*
@@ -238,6 +238,11 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
@NotNull
public CallArgumentsMapping analyzeCall(PyResolveContext resolveContext) {
return analyzeCall(resolveContext, 0);
}
@NotNull
public CallArgumentsMapping analyzeCall(PyResolveContext resolveContext, int offset) {
final CallArgumentsMappingImpl ret = new CallArgumentsMappingImpl(this);
// declaration-based checks
// proper arglist is: [positional,...][name=value,...][*tuple,][**dict]
@@ -245,7 +250,7 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
// following the spec: http://docs.python.org/ref/calls.html
PyCallExpression call = getCallExpression();
if (call != null) {
PyCallExpression.PyMarkedCallee resolved_callee = call.resolveCallee(resolveContext);
PyCallExpression.PyMarkedCallee resolved_callee = call.resolveCallee(resolveContext, offset);
if (resolved_callee != null) ret.mapArguments(resolved_callee, resolveContext.getTypeEvalContext());
}
return ret;
@@ -104,6 +104,11 @@ public class PyCallExpressionHelper {
@Nullable
public static PyCallExpression.PyMarkedCallee resolveCallee(PyCallExpression us, PyResolveContext resolveContext) {
return resolveCallee(us, resolveContext, 0);
}
@Nullable
public static PyCallExpression.PyMarkedCallee resolveCallee(PyCallExpression us, PyResolveContext resolveContext, int implicitOffset) {
PyFunction.Flag wrappedFlag = null;
boolean isConstructorCall = false;
@@ -152,8 +157,8 @@ public class PyCallExpressionHelper {
PyExpression lastQualifier = qualifiers != null && qualifiers.isEmpty() ? null : qualifiers.get(qualifiers.size()-1);
boolean isByClass = lastQualifier == null ? false : isQualifiedByClass((Callable)resolved, lastQualifier, resolveContext.getTypeEvalContext());
final Callable callable = (Callable)resolved;
int implicitOffset = getImplicitArgumentCount(callable, flags, isConstructorCall, isByInstance, isByClass);
implicitOffset += getImplicitArgumentCount(callable, flags, isConstructorCall, isByInstance, isByClass);
implicitOffset = implicitOffset < 0? 0: implicitOffset; // wrong source can trigger strange behaviour
return new PyCallExpression.PyMarkedCallee(callable, flags, implicitOffset,
resolveResult != null ? resolveResult.isImplicit() : false);
@@ -64,6 +64,10 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress
return PyCallExpressionHelper.resolveCallee(this, resolveContext);
}
public PyMarkedCallee resolveCallee(PyResolveContext resolveContext, int offset) {
return PyCallExpressionHelper.resolveCallee(this, resolveContext, offset);
}
public boolean isCalleeText(@NotNull String... nameCandidates) {
return PyCallExpressionHelper.isCalleeText(this, nameCandidates);
}
@@ -112,6 +112,9 @@ public class PyDecoratorImpl extends PyPresentableElementImpl<PyDecoratorStub> i
}
public PyMarkedCallee resolveCallee(PyResolveContext resolveContext) {
return resolveCallee(resolveContext, 0);
}
public PyMarkedCallee resolveCallee(PyResolveContext resolveContext, int offset) {
PyMarkedCallee callee = PyCallExpressionHelper.resolveCallee(this, resolveContext);
if (callee == null) return null;
if (!hasArgumentList()) {
@@ -7,6 +7,7 @@ import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.testing.AbstractPythonTestRunConfiguration;
import com.jetbrains.python.testing.VFSTestFrameworkListener;
import org.jdom.Element;
@@ -62,6 +63,6 @@ public class PythonAtTestRunConfiguration extends AbstractPythonTestRunConfigura
public void checkConfiguration() throws RuntimeConfigurationException {
super.checkConfiguration();
if (!VFSTestFrameworkListener.getInstance().isAtTestInstalled(getSdkHome()))
throw new RuntimeConfigurationWarning("No attest runner found in selected interpreter");
throw new RuntimeConfigurationWarning(PyBundle.message("runcfg.testing.no.test.framework", "attest"));
}
}
@@ -8,6 +8,7 @@ import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.JDOMExternalizerUtil;
import com.intellij.openapi.util.WriteExternalException;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.testing.AbstractPythonTestRunConfiguration;
import com.jetbrains.python.testing.VFSTestFrameworkListener;
import org.jdom.Element;
@@ -79,7 +80,7 @@ public class PythonNoseTestRunConfiguration extends AbstractPythonTestRunConfigu
public void checkConfiguration() throws RuntimeConfigurationException {
super.checkConfiguration();
if (!VFSTestFrameworkListener.getInstance().isNoseTestInstalled(getSdkHome()))
throw new RuntimeConfigurationWarning("No nosetest runner found in selected interpreter");
throw new RuntimeConfigurationWarning(PyBundle.message("runcfg.testing.no.test.framework", "nosetest"));
}
public boolean useParam() {
@@ -9,6 +9,7 @@ import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.JDOMExternalizerUtil;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.testing.AbstractPythonTestRunConfiguration;
import com.jetbrains.python.testing.VFSTestFrameworkListener;
import org.jdom.Element;
@@ -115,7 +116,7 @@ public class PyTestRunConfiguration extends AbstractPythonTestRunConfiguration i
throw new RuntimeConfigurationError("Please specify target folder or script");
}
if (!VFSTestFrameworkListener.getInstance().isPyTestInstalled(getSdkHome()))
throw new RuntimeConfigurationWarning("No py.test runner found in selected interpreter");
throw new RuntimeConfigurationWarning(PyBundle.message("runcfg.testing.no.test.framework", "py.test"));
}
@Override