From ecaa76f72f346d90586b6ee1029399262f6addf6 Mon Sep 17 00:00:00 2001 From: Denis Fokin Date: Mon, 18 May 2015 17:07:05 +0400 Subject: [PATCH 01/13] =?UTF-8?q?IDEA-140342=20AltGr+=D0=B3=20in=20ukraini?= =?UTF-8?q?an=20layout=20does=20not=20working?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../KeyboardSettingsExternalizable.java | 6 +++- .../src/com/intellij/ide/IdeEventQueue.java | 32 +++++++------------ 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/keymap/KeyboardSettingsExternalizable.java b/platform/platform-api/src/com/intellij/openapi/keymap/KeyboardSettingsExternalizable.java index 8f4c6f6b4329..e96abb6fd18f 100644 --- a/platform/platform-api/src/com/intellij/openapi/keymap/KeyboardSettingsExternalizable.java +++ b/platform/platform-api/src/com/intellij/openapi/keymap/KeyboardSettingsExternalizable.java @@ -35,7 +35,7 @@ import java.util.Locale; ) public class KeyboardSettingsExternalizable implements PersistentStateComponent { - private static final String [] supportedNonEnglishLanguages = {"de", "fr", "it"}; + private static final String [] supportedNonEnglishLanguages = {"de", "fr", "it", "uk"}; public static boolean isSupportedKeyboardLayout(@NotNull Component component) { if (SystemInfo.isMac) return false; @@ -92,6 +92,10 @@ public class KeyboardSettingsExternalizable implements PersistentStateComponent< myOptions = state; } + public boolean isUkrainianKeyboard (Component c) { + return c!=null && "uk".equals(c.getInputContext().getLocale().getLanguage()); + } + public boolean isNonEnglishKeyboardSupportEnabled () { return myOptions.USE_NON_ENGLISH_KEYBOARD; } diff --git a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java index f6a0b2d9e7fb..ad541c2675dc 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java @@ -416,20 +416,8 @@ public class IdeEventQueue extends EventQueue { if (e instanceof KeyEvent) { KeyEvent ke = (KeyEvent)e; - // Alt,keyChar=Undefined keyChar,modifiers=Ctrl+Alt,extModifiers=Ctrl+Alt+Button5,keyLocation=KEY_LOCATION_RIGHT - - //if (ke.getKeyCode() == KeyEvent.VK_ALT && ke.getID() == KeyEvent.KEY_PRESSED) { - // - // if (ke.getKeyLocation() == KeyEvent.KEY_LOCATION_RIGHT) { - // if ((ke.getModifiersEx() & (InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) != (InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) - // { - // altGrIsPressed = true; - // } - // } - // - //} else { - // altGrIsPressed = false; - //} + // Try to get it from editor + Component sourceComponent = WindowManagerEx.getInstanceEx().getMostRecentFocusedWindow(); if (ke.getID() == KeyEvent.KEY_PRESSED) { switch (ke.getKeyCode()) { @@ -459,8 +447,15 @@ public class IdeEventQueue extends EventQueue { } } - - + if (!leftAltIsPressed && KeyboardSettingsExternalizable.getInstance().isUkrainianKeyboard(sourceComponent) ) { + if ('ґ' == ke.getKeyChar() || ke.getKeyCode() == KeyEvent.VK_U) { + ke = new KeyEvent(ke.getComponent(), ke.getID(), ke.getWhen(), 0, + KeyEvent.VK_UNDEFINED, 'ґ', ke.getKeyLocation()); + ke.setKeyCode(KeyEvent.VK_U); + ke.setKeyChar('ґ'); + return ke; + } + } Integer keyCodeFromChar = CharToVKeyMap.get(ke.getKeyChar()); if (keyCodeFromChar != null) { @@ -469,15 +464,12 @@ public class IdeEventQueue extends EventQueue { ke.setKeyCode(keyCodeFromChar); } - // Try to get it from editor - Component sourceComponent = WindowManagerEx.getInstanceEx().getMostRecentFocusedWindow(); - //for (int i = 0; sourceComponent == null && i < WindowManagerEx.getInstanceEx().getAllProjectFrames().length; i++) { // sourceComponent = WindowManagerEx.getInstanceEx().getAllProjectFrames()[i].getComponent(); //} if (sourceComponent != null) { - if (sourceComponent.getInputContext().getLocale().getLanguage().equals("de")) { + if (KeyboardSettingsExternalizable.isSupportedKeyboardLayout(sourceComponent)) { if ((ke.getModifiersEx() & (InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) != 0 /*&& ke.getKeyLocation() == KeyEvent.KEY_LOCATION_RIGHT*/) { // On German keyboard layout on Windows we are getting on key press // ctrl + alt instead of AltGr From 344c7c74a80b2e467c21a3298546af1d2eb96513 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Wed, 13 May 2015 21:07:59 +0300 Subject: [PATCH 02/13] PY-15867 Initial cleanup in AddCallSuperQuickFix --- .../quickfix/AddCallSuperQuickFix.java | 77 +++++++++++-------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java index c8ac88af9da1..0243cf6c0463 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java @@ -32,10 +32,10 @@ import java.util.List; /** * For: * class B(A): - * def __init__(self): - * A.__init__(self) # inserted - * print "Constructor B was called" - * + * def __init__(self): + * A.__init__(self) # inserted + * print "Constructor B was called" + *

* User: catherine */ public class AddCallSuperQuickFix implements LocalQuickFix { @@ -52,7 +52,7 @@ public class AddCallSuperQuickFix implements LocalQuickFix { } public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) { - PyFunction problemFunction = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PyFunction.class); + final PyFunction problemFunction = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PyFunction.class); if (problemFunction == null) return; final StringBuilder superCall = new StringBuilder(); final PyClass klass = problemFunction.getContainingClass(); @@ -66,16 +66,18 @@ public class AddCallSuperQuickFix implements LocalQuickFix { boolean addComma = true; if (klass.isNewStyleClass()) { addComma = false; - if (LanguageLevel.forElement(klass).isPy3K()) + if (LanguageLevel.forElement(klass).isPy3K()) { superCall.append("super().__init__("); - else + } + else { superCall.append("super(").append(klass.getName()).append(", self).__init__("); + } } else { superCall.append(superClass.getName()); superCall.append(".__init__(self"); } - StringBuilder newFunction = new StringBuilder("def __init__(self"); + final StringBuilder newFunction = new StringBuilder("def __init__(self"); buildParameterList(problemFunction, superInit, superCall, newFunction, addComma); @@ -84,14 +86,16 @@ public class AddCallSuperQuickFix implements LocalQuickFix { PyExpression docstring = null; final PyStatement[] statements = statementList.getStatements(); if (statements.length != 0 && statements[0] instanceof PyExpressionStatement) { - PyExpressionStatement st = (PyExpressionStatement)statements[0]; - if (st.getExpression() instanceof PyStringLiteralExpression) + final PyExpressionStatement st = (PyExpressionStatement)statements[0]; + if (st.getExpression() instanceof PyStringLiteralExpression) { docstring = st.getExpression(); + } } newFunction.append("):\n\t"); - if (docstring != null) + if (docstring != null) { newFunction.append(docstring.getText()).append("\n\t"); + } newFunction.append(superCall).append("\n\t"); boolean first = true; for (PyStatement statement : statements) { @@ -102,22 +106,22 @@ public class AddCallSuperQuickFix implements LocalQuickFix { newFunction.append(statement.getText()).append("\n\t"); } - problemFunction.replace( - PyElementGenerator.getInstance(project).createFromText(LanguageLevel.forElement(problemFunction), PyFunction.class, - newFunction.toString())); + final PyElementGenerator generator = PyElementGenerator.getInstance(project); + problemFunction.replace(generator.createFromText(LanguageLevel.forElement(problemFunction), PyFunction.class, newFunction.toString())); } - private static void buildParameterList(@NotNull final PyFunction problemFunction, - @NotNull final PyFunction superInit, - @NotNull final StringBuilder superCall, - @NotNull final StringBuilder newFunction, boolean addComma) { + private static void buildParameterList(@NotNull PyFunction problemFunction, + @NotNull PyFunction superInit, + @NotNull StringBuilder superCall, + @NotNull StringBuilder newFunction, + boolean addComma) { final PyParameter[] parameters = problemFunction.getParameterList().getParameters(); final List problemParams = new ArrayList(); final List functionParams = new ArrayList(); String starName = null; String doubleStarName = null; for (int i = 1; i != parameters.length; i++) { - PyParameter p = parameters[i]; + final PyParameter p = parameters[i]; functionParams.add(p.getName()); if (p.getText().startsWith("**")) { doubleStarName = p.getText(); @@ -134,43 +138,52 @@ public class AddCallSuperQuickFix implements LocalQuickFix { newFunction.append(",").append(p.getText()); } - addParametersFromSuper(superInit, superCall, newFunction, addComma, problemParams, functionParams, starName, doubleStarName); + addParametersFromSuper(superInit, superCall, newFunction, problemParams, functionParams, starName, doubleStarName, addComma); } - private static void addParametersFromSuper(@NotNull final PyFunction superInit, @NotNull final StringBuilder superCall, - @NotNull final StringBuilder newFunction, boolean addComma, - @NotNull final List problemParams, @NotNull final List functionParams, - @Nullable String starName, @Nullable String doubleStarName) { + private static void addParametersFromSuper(@NotNull PyFunction superInit, + @NotNull StringBuilder superCall, + @NotNull StringBuilder newFunction, + @NotNull List problemParams, + @NotNull List functionParams, + @Nullable String starName, + @Nullable String doubleStarName, + boolean addComma) { final PyParameterList paramList = superInit.getParameterList(); - PyParameter[] parameters = paramList.getParameters(); + final PyParameter[] parameters = paramList.getParameters(); boolean addDouble = false; boolean addStar = false; for (int i = 1; i != parameters.length; i++) { - PyParameter p = parameters[i]; + final PyParameter p = parameters[i]; if (p.getDefaultValue() != null) continue; final String param = p.getName(); - String paramText = p.getText(); + final String paramText = p.getText(); if (paramText.startsWith("**")) { addDouble = true; - if (doubleStarName == null) + if (doubleStarName == null) { doubleStarName = p.getText(); + } continue; } if (paramText.startsWith("*")) { addStar = true; - if (starName == null) + if (starName == null) { starName = p.getText(); + } continue; } - if (addComma) + if (addComma) { superCall.append(","); + } superCall.append(param); - if (!functionParams.contains(param)) + if (!functionParams.contains(param)) { newFunction.append(",").append(param); + } addComma = true; } - for(String p : problemParams) + for (String p : problemParams) { newFunction.append(",").append(p); + } if (starName != null) { newFunction.append(",").append(starName); if (addStar) { From e75faff0f9af7378ef2f78a40f99021ac00296ed Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Thu, 14 May 2015 13:13:12 +0300 Subject: [PATCH 03/13] PY-15867 Improve AddCallSuperQuickFix to handle varargs and keyword-only parameters --- .../quickfix/AddCallSuperQuickFix.java | 299 +++++++++++++----- .../AddCallSuperKeywordOnlyParamInInit.py | 7 + ...ddCallSuperKeywordOnlyParamInInit_after.py | 7 + ...AddCallSuperKeywordOnlyParamInSuperInit.py | 8 + ...lSuperKeywordOnlyParamInSuperInit_after.py | 8 + .../AddCallSuperSingleStarParamInSuperInit.py | 7 + ...ngleStarParamInSuperInitAndVarargInInit.py | 7 + ...arParamInSuperInitAndVarargInInit_after.py | 7 + ...llSuperSingleStarParamInSuperInit_after.py | 7 + .../com/jetbrains/python/PyQuickFixTest.java | 43 +++ 10 files changed, 320 insertions(+), 80 deletions(-) create mode 100644 python/testData/inspections/AddCallSuperKeywordOnlyParamInInit.py create mode 100644 python/testData/inspections/AddCallSuperKeywordOnlyParamInInit_after.py create mode 100644 python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit.py create mode 100644 python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit_after.py create mode 100644 python/testData/inspections/AddCallSuperSingleStarParamInSuperInit.py create mode 100644 python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit.py create mode 100644 python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit_after.py create mode 100644 python/testData/inspections/AddCallSuperSingleStarParamInSuperInit_after.py diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java index 0243cf6c0463..52087e9fbcb3 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java @@ -18,7 +18,11 @@ package com.jetbrains.python.inspections.quickfix; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Couple; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.*; @@ -63,7 +67,7 @@ public class AddCallSuperQuickFix implements LocalQuickFix { final PyClass superClass = superClasses[0]; final PyFunction superInit = superClass.findMethodByName(PyNames.INIT, true); if (superInit == null) return; - boolean addComma = true; + final boolean addComma; if (klass.isNewStyleClass()) { addComma = false; if (LanguageLevel.forElement(klass).isPy3K()) { @@ -74,14 +78,28 @@ public class AddCallSuperQuickFix implements LocalQuickFix { } } else { + addComma = true; superCall.append(superClass.getName()); superCall.append(".__init__(self"); } final StringBuilder newFunction = new StringBuilder("def __init__(self"); - buildParameterList(problemFunction, superInit, superCall, newFunction, addComma); + final Couple> couple = buildNewFunctionParamsAndSuperInitCallArgs(problemFunction, superInit); + final List newParameters = couple.getFirst(); + if (!newParameters.isEmpty()) { + newFunction.append(", "); + } + StringUtil.join(newParameters, ", ", newFunction); + newFunction.append("):\n\t"); + + final List superCallArguments = couple.getSecond(); + if (addComma && !superCallArguments.isEmpty()) { + superCall.append(", "); + } + StringUtil.join(superCallArguments, ", ", superCall); superCall.append(")"); + final PyStatementList statementList = problemFunction.getStatementList(); PyExpression docstring = null; final PyStatement[] statements = statementList.getStatements(); @@ -92,7 +110,6 @@ public class AddCallSuperQuickFix implements LocalQuickFix { } } - newFunction.append("):\n\t"); if (docstring != null) { newFunction.append(docstring.getText()).append("\n\t"); } @@ -110,94 +127,216 @@ public class AddCallSuperQuickFix implements LocalQuickFix { problemFunction.replace(generator.createFromText(LanguageLevel.forElement(problemFunction), PyFunction.class, newFunction.toString())); } - private static void buildParameterList(@NotNull PyFunction problemFunction, - @NotNull PyFunction superInit, - @NotNull StringBuilder superCall, - @NotNull StringBuilder newFunction, - boolean addComma) { - final PyParameter[] parameters = problemFunction.getParameterList().getParameters(); - final List problemParams = new ArrayList(); - final List functionParams = new ArrayList(); - String starName = null; - String doubleStarName = null; - for (int i = 1; i != parameters.length; i++) { - final PyParameter p = parameters[i]; - functionParams.add(p.getName()); - if (p.getText().startsWith("**")) { - doubleStarName = p.getText(); - continue; + @NotNull + private static Couple> buildNewFunctionParamsAndSuperInitCallArgs(@NotNull PyFunction origInit, + @NotNull PyFunction superInit) { + final List newFunctionParams = new ArrayList(); + final List superCallArgs = new ArrayList(); + + final ParametersInfo origInfo = new ParametersInfo(origInit.getParameterList()); + final ParametersInfo superInfo = new ParametersInfo(superInit.getParameterList()); + + // Required parameters (not-keyword) + for (PyParameter param : origInfo.getRequiredParameters()) { + newFunctionParams.add(param.getText()); + } + for (PyParameter param : superInfo.getRequiredParameters()) { + if (!origInfo.containsRequiredParam(param.getName())) { + newFunctionParams.add(param.getText()); } - if (p.getText().startsWith("*")) { - starName = p.getText(); - continue; - } - if (p.getDefaultValue() != null) { - problemParams.add(p.getText()); - continue; - } - newFunction.append(",").append(p.getText()); + superCallArgs.add(param.getName()); } - addParametersFromSuper(superInit, superCall, newFunction, problemParams, functionParams, starName, doubleStarName, addComma); + // Optional parameters (not-keyword) + for (PyParameter param : origInfo.getOptionalParameters()) { + newFunctionParams.add(param.getText()); + } + + // Positional vararg + PyParameter starredParam = null; + if (origInfo.getPositionalContainerParameter() != null) { + starredParam = origInfo.getPositionalContainerParameter(); + } + else if (superInfo.getPositionalContainerParameter() != null) { + starredParam = superInfo.getPositionalContainerParameter(); + } + else if (origInfo.getSingleStarParameter() != null) { + starredParam = origInfo.getSingleStarParameter(); + } + else if (superInfo.getSingleStarParameter() != null) { + starredParam = superInfo.getSingleStarParameter(); + } + if (starredParam != null) { + newFunctionParams.add(starredParam.getText()); + if (superInfo.getPositionalContainerParameter() != null) { + superCallArgs.add("*" + starredParam.getName()); + } + } + + // Required keyword-only parameters + for (PyParameter param : origInfo.getRequiredKeywordOnlyParameters()) { + newFunctionParams.add(param.getText()); + } + for (PyParameter param : superInfo.getRequiredKeywordOnlyParameters()) { + if (!origInfo.containsRequiredKeywordOnlyParameter(param.getName())) { + newFunctionParams.add(param.getText()); + } + superCallArgs.add(param.getName() + "=" + param.getName()); + } + + // Optional keyword-only parameters + for (PyParameter param : origInfo.getOptionalKeywordOnlyParameters()) { + newFunctionParams.add(param.getText()); + } + + // Keyword vararg + PyParameter doubleStarredParam = null; + if (origInfo.getKeywordContainerParameter() != null) { + doubleStarredParam = origInfo.getKeywordContainerParameter(); + } + else if (superInfo.getKeywordContainerParameter() != null) { + doubleStarredParam = superInfo.getKeywordContainerParameter(); + } + if (doubleStarredParam != null) { + newFunctionParams.add(doubleStarredParam.getText()); + if (superInfo.getKeywordContainerParameter() != null) { + superCallArgs.add("**" + doubleStarredParam.getName()); + } + } + return Couple.of(newFunctionParams, superCallArgs); } - private static void addParametersFromSuper(@NotNull PyFunction superInit, - @NotNull StringBuilder superCall, - @NotNull StringBuilder newFunction, - @NotNull List problemParams, - @NotNull List functionParams, - @Nullable String starName, - @Nullable String doubleStarName, - boolean addComma) { - final PyParameterList paramList = superInit.getParameterList(); - final PyParameter[] parameters = paramList.getParameters(); - boolean addDouble = false; - boolean addStar = false; - for (int i = 1; i != parameters.length; i++) { - final PyParameter p = parameters[i]; - if (p.getDefaultValue() != null) continue; - final String param = p.getName(); - final String paramText = p.getText(); - if (paramText.startsWith("**")) { - addDouble = true; - if (doubleStarName == null) { - doubleStarName = p.getText(); + private static class ParametersInfo { + + private final PyParameter mySelfParam; + /** + * Parameters without default value that come before first "*..." parameter. + */ + private final List myRequiredParams = new ArrayList(); + /** + * Parameters with default value that come before first "*..." parameter. + */ + private final List myOptionalParams = new ArrayList(); + /** + * Parameter of form "*args" (positional vararg), not the same as single "*". + */ + private final PyParameter myPositionalContainerParam; + /** + * Parameter "*", that is used to delimit normal and keyword-only parameters. + */ + private final PyParameter mySingleStarParam; + /** + * Parameters without default value that come after first "*..." parameter. + */ + private final List myRequiredKwOnlyParams = new ArrayList(); + /** + * Parameters with default value that come after first "*..." parameter. + */ + private final List myOptionalKwOnlyParams = new ArrayList(); + /** + * Parameter of form "**kwargs" (keyword vararg). + */ + private final PyParameter myKeywordContainerParam; + + public ParametersInfo(@NotNull PyParameterList parameterList) { + PyParameter positionalContainer = null; + PyParameter singleStarParam = null; + PyParameter keywordContainer = null; + PyParameter selfParam = null; + + for (PyParameter param : parameterList.getParameters()) { + if (param.isSelf()) { + selfParam = param; } - continue; - } - if (paramText.startsWith("*")) { - addStar = true; - if (starName == null) { - starName = p.getText(); + else if (param.getText().equals("*")) { + singleStarParam = param; + } + else if (param.getText().startsWith("**")) { + keywordContainer = param; + } + else if (param.getText().startsWith("*")) { + positionalContainer = param; + } + else if (positionalContainer == null && singleStarParam == null) { + if (param.hasDefaultValue()) { + myOptionalParams.add(param); + } + else { + myRequiredParams.add(param); + } + } + else { + if (param.hasDefaultValue()) { + myOptionalKwOnlyParams.add(param); + } + else { + myRequiredKwOnlyParams.add(param); + } } - continue; } - if (addComma) { - superCall.append(","); - } - superCall.append(param); - if (!functionParams.contains(param)) { - newFunction.append(",").append(param); - } - addComma = true; + + mySelfParam = selfParam; + myPositionalContainerParam = positionalContainer; + mySingleStarParam = singleStarParam; + myKeywordContainerParam = keywordContainer; } - for (String p : problemParams) { - newFunction.append(",").append(p); + + public boolean containsRequiredParam(@Nullable final String name) { + return ContainerUtil.exists(myRequiredParams, new Condition() { + @Override + public boolean value(PyParameter parameter) { + return name != null && name.equals(parameter.getName()); + } + }); } - if (starName != null) { - newFunction.append(",").append(starName); - if (addStar) { - if (addComma) superCall.append(","); - superCall.append(starName); - addComma = true; - } + + public boolean containsRequiredKeywordOnlyParameter(@Nullable final String name) { + return ContainerUtil.exists(myRequiredKwOnlyParams, new Condition() { + @Override + public boolean value(PyParameter parameter) { + return name != null && name.equals(parameter.getName()); + } + }); } - if (doubleStarName != null) { - newFunction.append(",").append(doubleStarName); - if (addDouble) { - if (addComma) superCall.append(","); - superCall.append(doubleStarName); - } + + @Nullable + public PyParameter getSelfParameter() { + return mySelfParam; + } + + @NotNull + public List getRequiredParameters() { + return myRequiredParams; + } + + @NotNull + public List getOptionalParameters() { + return myOptionalParams; + } + + @Nullable + public PyParameter getPositionalContainerParameter() { + return myPositionalContainerParam; + } + + @Nullable + public PyParameter getSingleStarParameter() { + return mySingleStarParam; + } + + @NotNull + public List getRequiredKeywordOnlyParameters() { + return myRequiredKwOnlyParams; + } + + @NotNull + public List getOptionalKeywordOnlyParameters() { + return myOptionalKwOnlyParams; + } + + @Nullable + public PyParameter getKeywordContainerParameter() { + return myKeywordContainerParam; } } } diff --git a/python/testData/inspections/AddCallSuperKeywordOnlyParamInInit.py b/python/testData/inspections/AddCallSuperKeywordOnlyParamInInit.py new file mode 100644 index 000000000000..e1da8962d217 --- /dev/null +++ b/python/testData/inspections/AddCallSuperKeywordOnlyParamInInit.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, a): + pass + +class B(A): + def __init__(self, b, c=1, *args, kw_only): + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperKeywordOnlyParamInInit_after.py b/python/testData/inspections/AddCallSuperKeywordOnlyParamInInit_after.py new file mode 100644 index 000000000000..925cac6746a3 --- /dev/null +++ b/python/testData/inspections/AddCallSuperKeywordOnlyParamInInit_after.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, a): + pass + +class B(A): + def __init__(self, b, a, c=1, *args, kw_only): + super().__init__(a) \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit.py b/python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit.py new file mode 100644 index 000000000000..5248dd92742a --- /dev/null +++ b/python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit.py @@ -0,0 +1,8 @@ +class A: + def __init__(self, a, b=1, *args, kw_only): + pass + + +class B(A): + def __init__(self, c): + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit_after.py b/python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit_after.py new file mode 100644 index 000000000000..0ee340e47d36 --- /dev/null +++ b/python/testData/inspections/AddCallSuperKeywordOnlyParamInSuperInit_after.py @@ -0,0 +1,8 @@ +class A: + def __init__(self, a, b=1, *args, kw_only): + pass + + +class B(A): + def __init__(self, c, a, *args, kw_only): + super().__init__(a, *args, kw_only=kw_only) \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperSingleStarParamInSuperInit.py b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInit.py new file mode 100644 index 000000000000..7ff489252191 --- /dev/null +++ b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInit.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, *, kw_only, optional_kw_only=None): + pass + +class B(A): + def __init__(self): + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit.py b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit.py new file mode 100644 index 000000000000..afca366ff1e3 --- /dev/null +++ b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, *, kw_only): + pass + +class B(A): + def __init__(self, *args, another_kw_only): + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit_after.py b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit_after.py new file mode 100644 index 000000000000..01cf279b21fc --- /dev/null +++ b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInitAndVarargInInit_after.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, *, kw_only): + pass + +class B(A): + def __init__(self, *args, another_kw_only, kw_only): + super().__init__(kw_only=kw_only) \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperSingleStarParamInSuperInit_after.py b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInit_after.py new file mode 100644 index 000000000000..1eb8c4228300 --- /dev/null +++ b/python/testData/inspections/AddCallSuperSingleStarParamInSuperInit_after.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, *, kw_only, optional_kw_only=None): + pass + +class B(A): + def __init__(self, *, kw_only): + super().__init__(kw_only=kw_only) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 9a55254d8287..6ca5fe3e4211 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -360,6 +360,49 @@ public class PyQuickFixTest extends PyTestCase { PyBundle.message("QFIX.add.super"), true, true); } + + // PY-15867 + public void testAddCallSuperKeywordOnlyParamInSuperInit() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + public void run() { + doInspectionTest("AddCallSuperKeywordOnlyParamInSuperInit.py", PyMissingConstructorInspection.class, + PyBundle.message("QFIX.add.super"), true, true); + } + }); + } + + // PY-15867 + public void testAddCallSuperKeywordOnlyParamInInit() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + public void run() { + doInspectionTest("AddCallSuperKeywordOnlyParamInInit.py", PyMissingConstructorInspection.class, + PyBundle.message("QFIX.add.super"), true, true); + } + }); + } + + // PY-15867 + public void testAddCallSuperSingleStarParamInSuperInit() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + public void run() { + doInspectionTest("AddCallSuperSingleStarParamInSuperInit.py", PyMissingConstructorInspection.class, + PyBundle.message("QFIX.add.super"), true, true); + } + }); + } + + // PY-15867 + public void testAddCallSuperSingleStarParamInSuperInitAndVarargInInit() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + @Override + public void run() { + doInspectionTest("AddCallSuperSingleStarParamInSuperInitAndVarargInInit.py", PyMissingConstructorInspection.class, + PyBundle.message("QFIX.add.super"), true, true); + } + }); + + } + //PY-491, PY-13297 public void testAddEncoding() { doInspectionTest("AddEncoding.py", PyMandatoryEncodingInspection.class, From 482009a18020164dbeec94a0cdcd1a9de64f9966 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Fri, 15 May 2015 01:44:00 +0300 Subject: [PATCH 04/13] PY-11561 Preserve existing type annotations in the modified constructor --- .../inspections/quickfix/AddCallSuperQuickFix.java | 6 +++++- .../inspections/AddCallSuperAnnotations_after.py | 2 +- .../AddCallSuperTypeAnnotationsPreserved.py | 7 +++++++ .../AddCallSuperTypeAnnotationsPreserved_after.py | 7 +++++++ .../testSrc/com/jetbrains/python/PyQuickFixTest.java | 10 ++++++++++ 5 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 python/testData/inspections/AddCallSuperTypeAnnotationsPreserved.py create mode 100644 python/testData/inspections/AddCallSuperTypeAnnotationsPreserved_after.py diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java index 52087e9fbcb3..193104bdd3bb 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java @@ -91,7 +91,11 @@ public class AddCallSuperQuickFix implements LocalQuickFix { newFunction.append(", "); } StringUtil.join(newParameters, ", ", newFunction); - newFunction.append("):\n\t"); + newFunction.append(")"); + if (problemFunction.getAnnotation() != null) { + newFunction.append(problemFunction.getAnnotation().getText()); + } + newFunction.append(":\n\t"); final List superCallArguments = couple.getSecond(); if (addComma && !superCallArguments.isEmpty()) { diff --git a/python/testData/inspections/AddCallSuperAnnotations_after.py b/python/testData/inspections/AddCallSuperAnnotations_after.py index 4bfd08c375e3..ddee178f3ace 100644 --- a/python/testData/inspections/AddCallSuperAnnotations_after.py +++ b/python/testData/inspections/AddCallSuperAnnotations_after.py @@ -4,5 +4,5 @@ class Example1: class Example2(Example1): - def __init__(self, field1): + def __init__(self, field1: str): super().__init__(field1) \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperTypeAnnotationsPreserved.py b/python/testData/inspections/AddCallSuperTypeAnnotationsPreserved.py new file mode 100644 index 000000000000..76b33e20063e --- /dev/null +++ b/python/testData/inspections/AddCallSuperTypeAnnotationsPreserved.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, a:int, b:float, *args:tuple, c:complex, **kwargs:dict) -> None: + pass + +class B(A): + def __init__(self, d:str, *, e:bytes) -> list: + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperTypeAnnotationsPreserved_after.py b/python/testData/inspections/AddCallSuperTypeAnnotationsPreserved_after.py new file mode 100644 index 000000000000..c8b5b3e67c00 --- /dev/null +++ b/python/testData/inspections/AddCallSuperTypeAnnotationsPreserved_after.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, a:int, b:float, *args:tuple, c:complex, **kwargs:dict) -> None: + pass + +class B(A): + def __init__(self, d:str, a:int, b:float, *args:tuple, e:bytes, c:complex, **kwargs:dict) -> list: + super().__init__(a, b, *args, c=c, **kwargs) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 6ca5fe3e4211..99a0dc27efbb 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -400,7 +400,17 @@ public class PyQuickFixTest extends PyTestCase { PyBundle.message("QFIX.add.super"), true, true); } }); + } + // PY-15867 + public void testAddCallSuperTypeAnnotationsPreserved() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + @Override + public void run() { + doInspectionTest("AddCallSuperTypeAnnotationsPreserved.py", PyMissingConstructorInspection.class, + PyBundle.message("QFIX.add.super"), true, true); + } + }); } //PY-491, PY-13297 From e3592fe5c4b75242cc60b057fd2156268a17cf59 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 18 May 2015 12:42:44 +0300 Subject: [PATCH 05/13] PY-15867 Clean up in PyQuickFixTest and Py3QuickFixTest * Introduce overloaded version of doInspectionTest that doesn't require name of test data file * Add a few missing request IDs and place existing ones consistently above corresponding methods * Remove redundant "Py" and "QuickFix" parts from test names --- .../com/jetbrains/python/Py3QuickFixTest.java | 45 ++- .../com/jetbrains/python/PyQuickFixTest.java | 326 +++++++++--------- 2 files changed, 190 insertions(+), 181 deletions(-) diff --git a/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java b/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java index f757da9f3fac..6d7389607e5c 100644 --- a/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java @@ -24,6 +24,7 @@ import com.jetbrains.python.inspections.PyMissingConstructorInspection; import com.jetbrains.python.inspections.PyStatementEffectInspection; import com.jetbrains.python.psi.LanguageLevel; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; @TestDataPath("$CONTENT_ROOT/../testData/inspections/") public class Py3QuickFixTest extends PyTestCase { @@ -32,39 +33,42 @@ public class Py3QuickFixTest extends PyTestCase { return PyTestCase.ourPy3Descriptor; } + // PY-13685 public void testReplacePrintEnd() { runWithLanguageLevel(LanguageLevel.PYTHON34, new Runnable() { @Override public void run() { - doInspectionTest("ReplacePrintEnd.py", PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); - }}); + doInspectionTest(PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); + } + }); } + // PY-13685 public void testReplacePrintComment() { runWithLanguageLevel(LanguageLevel.PYTHON34, new Runnable() { @Override public void run() { - doInspectionTest("ReplacePrintComment.py", PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, - true); - }}); + doInspectionTest(PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); + } + }); } + // PY-13685 public void testReplaceExecComment() { runWithLanguageLevel(LanguageLevel.PYTHON34, new Runnable() { @Override public void run() { - doInspectionTest("ReplaceExecComment.py", PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, - true); - }}); + doInspectionTest(PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); + } + }); } + // PY-11561 public void testAddCallSuperAnnotations() { runWithLanguageLevel(LanguageLevel.PYTHON33, new Runnable() { @Override public void run() { - doInspectionTest("AddCallSuperAnnotations.py", - PyMissingConstructorInspection.class, - PyBundle.message("QFIX.add.super"), true, true); + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } }); } @@ -75,9 +79,16 @@ public class Py3QuickFixTest extends PyTestCase { return PythonTestUtil.getTestDataPath() + "/inspections/"; } - protected void doInspectionTest(@TestDataFile @NonNls String testFileName, - final Class inspectionClass, - @NonNls String quickFixName, + private void doInspectionTest(@NotNull Class inspectionClass, + @NotNull String quickFixName, + boolean applyFix, + boolean available) { + doInspectionTest(getTestName(false) + ".py", inspectionClass, quickFixName, applyFix, available); + } + + protected void doInspectionTest(@TestDataFile @NonNls @NotNull String testFileName, + @NotNull Class inspectionClass, + @NonNls @NotNull String quickFixName, boolean applyFix, boolean available) { doInspectionTest(new String[]{testFileName}, inspectionClass, quickFixName, applyFix, available); @@ -93,9 +104,9 @@ public class Py3QuickFixTest extends PyTestCase { * @param available true if the fix should be available, false if it should be explicitly not available. * @throws Exception */ - protected void doInspectionTest(@NonNls String[] testFiles, - final Class inspectionClass, - @NonNls String quickFixName, + protected void doInspectionTest(@NonNls @NotNull String[] testFiles, + @NotNull Class inspectionClass, + @NonNls @NotNull String quickFixName, boolean applyFix, boolean available) { myFixture.enableInspections(inspectionClass); diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 99a0dc27efbb..05f8258b0e68 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -27,6 +27,7 @@ import com.jetbrains.python.inspections.*; import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection; import com.jetbrains.python.psi.LanguageLevel; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; /** * @author dcheryasov @@ -47,15 +48,19 @@ public class PyQuickFixTest extends PyTestCase { } public void testAddImport() { - doInspectionTest(new String[] { "AddImport.py", "ImportTarget.py" }, PyUnresolvedReferencesInspection.class, "Import 'ImportTarget'", true, true); + doInspectionTest(new String[]{"AddImport.py", "ImportTarget.py"}, PyUnresolvedReferencesInspection.class, + "Import 'ImportTarget'", true, true); } public void testAddImportDoc() { - doInspectionTest(new String[] { "AddImportDoc.py", "ImportTarget.py" }, PyUnresolvedReferencesInspection.class, "Import 'ImportTarget'", true, true); + doInspectionTest(new String[]{"AddImportDoc.py", "ImportTarget.py"}, PyUnresolvedReferencesInspection.class, + "Import 'ImportTarget'", true, true); } - public void testAddImportDocComment() { // PY-728 - doInspectionTest(new String[] { "AddImportDocComment.py", "ImportTarget.py" }, PyUnresolvedReferencesInspection.class, "Import 'ImportTarget'", true, true); + // PY-728 + public void testAddImportDocComment() { + doInspectionTest(new String[]{"AddImportDocComment.py", "ImportTarget.py"}, PyUnresolvedReferencesInspection.class, + "Import 'ImportTarget'", true, true); } public void testImportFromModule() { @@ -73,7 +78,8 @@ public class PyQuickFixTest extends PyTestCase { assertEquals("Import 'module.MyOldStyleClass'", intention.getText()); } - public void testImportFromModuleStar() { // PY-6302 + // PY-6302 + public void testImportFromModuleStar() { myFixture.enableInspections(PyUnresolvedReferencesInspection.class); myFixture.copyDirectoryToProject("importFromModuleStar", ""); myFixture.configureFromTempProjectFile("source.py"); @@ -91,7 +97,8 @@ public class PyQuickFixTest extends PyTestCase { settings.PREFER_FROM_IMPORT = false; settings.HIGHLIGHT_UNUSED_IMPORTS = false; try { - doInspectionTest(new String[]{"QualifyByImport.py", "QualifyByImportFoo.py"}, PyUnresolvedReferencesInspection.class, PyBundle.message("ACT.qualify.with.module"), true, true); + doInspectionTest(new String[]{"QualifyByImport.py", "QualifyByImportFoo.py"}, PyUnresolvedReferencesInspection.class, + PyBundle.message("ACT.qualify.with.module"), true, true); } finally { settings.PREFER_FROM_IMPORT = oldPreferFrom; @@ -104,7 +111,8 @@ public class PyQuickFixTest extends PyTestCase { boolean oldHighlightUnused = settings.HIGHLIGHT_UNUSED_IMPORTS; settings.HIGHLIGHT_UNUSED_IMPORTS = false; try { - doInspectionTest(new String[]{"AddToImportFromList.py", "AddToImportFromFoo.py"}, PyUnresolvedReferencesInspection.class, "Import 'foo(a) from AddToImportFromFoo'", true, true); + doInspectionTest(new String[]{"AddToImportFromList.py", "AddToImportFromFoo.py"}, PyUnresolvedReferencesInspection.class, + "Import 'foo(a) from AddToImportFromFoo'", true, true); } finally { settings.HIGHLIGHT_UNUSED_IMPORTS = oldHighlightUnused; @@ -115,90 +123,86 @@ public class PyQuickFixTest extends PyTestCase { // TODO: add tests for stub indexes-based autoimport of unimported somehow. public void testAddSelf() { - doInspectionTest("AddSelf.py", PyMethodParametersInspection.class, PyBundle.message("QFIX.add.parameter.self", "self"), true, true); + doInspectionTest(PyMethodParametersInspection.class, PyBundle.message("QFIX.add.parameter.self", "self"), true, true); } public void testReplacePrint() { - doInspectionTest("ReplacePrint.py", PyCompatibilityInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); + doInspectionTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); } - public void testAddSelfFunction() { //PY-4556 - doInspectionTest("AddSelfFunction.py", - PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.unresolved.reference", "get_a", "self"), true, true); + // PY-4556 + public void testAddSelfFunction() { + doInspectionTest("AddSelfFunction.py", PyUnresolvedReferencesInspection.class, + PyBundle.message("QFIX.unresolved.reference", "get_a", "self"), true, true); } - public void testAddSelfToClassmethod() { //PY-9721 - doInspectionTest("AddSelfToClassmethod.py", - PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.unresolved.reference", "foo", "cls"), true, true); + // PY-9721 + public void testAddSelfToClassmethod() { + doInspectionTest("AddSelfToClassmethod.py", PyUnresolvedReferencesInspection.class, + PyBundle.message("QFIX.unresolved.reference", "foo", "cls"), true, true); } public void testAddCls() { - doInspectionTest("AddCls.py", PyMethodParametersInspection.class, PyBundle.message("QFIX.add.parameter.self", "cls"), true, true); + doInspectionTest(PyMethodParametersInspection.class, PyBundle.message("QFIX.add.parameter.self", "cls"), true, true); } public void testRenameToSelf() { - doInspectionTest("RenameToSelf.py", PyMethodParametersInspection.class, PyBundle.message("QFIX.rename.parameter.to.$0", "self"), true, - true); + doInspectionTest(PyMethodParametersInspection.class, PyBundle.message("QFIX.rename.parameter.to.$0", "self"), true, true); } public void testRemoveTrailingSemicolon() { - doInspectionTest("RemoveTrailingSemicolon.py", PyTrailingSemicolonInspection.class, PyBundle.message("QFIX.remove.trailing.semicolon"), - true, true); + doInspectionTest(PyTrailingSemicolonInspection.class, PyBundle.message("QFIX.remove.trailing.semicolon"), true, true); } public void testDictCreation() { - doInspectionTest("DictCreation.py", PyDictCreationInspection.class, PyBundle.message("QFIX.dict.creation"), true, true); + doInspectionTest(PyDictCreationInspection.class, PyBundle.message("QFIX.dict.creation"), true, true); } - public void testDictCreationTuple() { //PY-6283 - doInspectionTest("DictCreationTuple.py", PyDictCreationInspection.class, PyBundle.message("QFIX.dict.creation"), true, true); + // PY-6283 + public void testDictCreationTuple() { + doInspectionTest(PyDictCreationInspection.class, PyBundle.message("QFIX.dict.creation"), true, true); } - public void testDictCreationDuplicate() { //PY-7318 - doInspectionTest("DictCreationDuplicate.py", PyDictCreationInspection.class, PyBundle.message("QFIX.dict.creation"), true, true); + // PY-7318 + public void testDictCreationDuplicate() { + doInspectionTest(PyDictCreationInspection.class, PyBundle.message("QFIX.dict.creation"), true, true); } public void testTransformClassicClass() { - doInspectionTest("TransformClassicClass.py", PyClassicStyleClassInspection.class, - PyBundle.message("QFIX.classic.class.transform"), true, true); + doInspectionTest(PyClassicStyleClassInspection.class, PyBundle.message("QFIX.classic.class.transform"), true, true); } - public void testAddGlobalQuickFix() { - doInspectionTest("AddGlobalStatement.py", PyUnboundLocalVariableInspection.class, - PyBundle.message("QFIX.add.global"), true, true); + public void testAddGlobalStatement() { + doInspectionTest(PyUnboundLocalVariableInspection.class, PyBundle.message("QFIX.add.global"), true, true); } - public void testAddGlobalExistingQuickFix() { - doInspectionTest("AddGlobalExistingStatement.py", PyUnboundLocalVariableInspection.class, - PyBundle.message("QFIX.add.global"), true, true); + public void testAddGlobalExistingStatement() { + doInspectionTest(PyUnboundLocalVariableInspection.class, PyBundle.message("QFIX.add.global"), true, true); } - public void testSimplifyBooleanCheckQuickFix() { - doInspectionTest("SimplifyBooleanCheck.py", PySimplifyBooleanCheckInspection.class, - PyBundle.message("QFIX.simplify.$0", "b"), true, true); + public void testSimplifyBooleanCheck() { + doInspectionTest(PySimplifyBooleanCheckInspection.class, PyBundle.message("QFIX.simplify.$0", "b"), true, true); } - public void testFromFutureImportQuickFix() { - doInspectionTest("MoveFromFutureImport.py", PyFromFutureImportInspection.class, - PyBundle.message("QFIX.move.from.future.import"), true, true); + public void testMoveFromFutureImport() { + doInspectionTest(PyFromFutureImportInspection.class, PyBundle.message("QFIX.move.from.future.import"), true, true); } - public void testFromFutureImportQuickFixDocString() { // PY-10080 - doInspectionTest("MoveFromFutureImportDocString.py", PyFromFutureImportInspection.class, - PyBundle.message("QFIX.move.from.future.import"), true, true); + // PY-10080 + public void testMoveFromFutureImportDocString() { + doInspectionTest(PyFromFutureImportInspection.class, PyBundle.message("QFIX.move.from.future.import"), true, true); } - public void testComparisonWithNoneQuickFix() { - doInspectionTest("ComparisonWithNone.py", PyComparisonWithNoneInspection.class, - PyBundle.message("QFIX.replace.equality"), true, true); + public void testComparisonWithNone() { + doInspectionTest(PyComparisonWithNoneInspection.class, PyBundle.message("QFIX.replace.equality"), true, true); } public void testAddClassFix() { doInspectionTest("AddClass.py", PyUnresolvedReferencesInspection.class, "Create class 'Xyzzy'", true, true); } - - public void testAddFunctionToModule() { // PY-1602 + // PY-1602 + public void testAddFunctionToModule() { doInspectionTest( "AddFunctionToModule.py", PyUnresolvedReferencesInspection.class, @@ -207,7 +211,8 @@ public class PyQuickFixTest extends PyTestCase { ); } - public void testRedundantParentheses() { // PY-1470 + // PY-1470 + public void testRedundantParentheses() { String[] testFiles = new String[]{"RedundantParentheses.py"}; myFixture.enableInspections(PyRedundantParenthesesInspection.class); myFixture.configureByFiles(testFiles); @@ -216,88 +221,86 @@ public class PyQuickFixTest extends PyTestCase { assertNotNull(intentionAction); myFixture.launchAction(intentionAction); myFixture.checkResultByFile(graftBeforeExt(testFiles[0], "_after")); - } - public void testRedundantParenthesesBoolean() { // PY-3095 - doInspectionTest("RedundantParenthesesBoolean.py", PyRedundantParenthesesInspection.class, - PyBundle.message("QFIX.redundant.parentheses"), true, true); + // PY-3095 + public void testRedundantParenthesesBoolean() { + doInspectionTest(PyRedundantParenthesesInspection.class, PyBundle.message("QFIX.redundant.parentheses"), true, true); } - public void testRedundantParenthesesMore() { // PY-3239 - doInspectionTest("RedundantParenthesesMore.py", PyRedundantParenthesesInspection.class, - PyBundle.message("QFIX.redundant.parentheses"), true, true); + // PY-3239 + public void testRedundantParenthesesMore() { + doInspectionTest(PyRedundantParenthesesInspection.class, PyBundle.message("QFIX.redundant.parentheses"), true, true); } - public void testRedundantParenthesesParenthesizedExpression() { // PY-12679 - doInspectionTest("RedundantParenthesesParenthesizedExpression.py", PyRedundantParenthesesInspection.class, - PyBundle.message("QFIX.redundant.parentheses"), true, true); + // PY-12679 + public void testRedundantParenthesesParenthesizedExpression() { + doInspectionTest(PyRedundantParenthesesInspection.class, PyBundle.message("QFIX.redundant.parentheses"), true, true); } // PY-15506 public void testEmptyListOfBaseClasses() { - doInspectionTest("EmptyListOfBaseClasses.py", PyRedundantParenthesesInspection.class, - PyBundle.message("QFIX.redundant.parentheses"), true, true); + doInspectionTest(PyRedundantParenthesesInspection.class, PyBundle.message("QFIX.redundant.parentheses"), true, true); } - public void testChainedComparisons() { // PY-1020 - doInspectionTest("ChainedComparisons.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + // PY-1020 + public void testChainedComparisons() { + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } - public void testChainedComparison1() { // PY-3126 - doInspectionTest("ChainedComparison1.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + // PY-3126 + public void testChainedComparison1() { + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } - public void testChainedComparison2() { // PY-3126 - doInspectionTest("ChainedComparison2.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + // PY-3126 + public void testChainedComparison2() { + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } - public void testChainedComparison3() { // PY-3126 - doInspectionTest("ChainedComparison3.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + // PY-3126 + public void testChainedComparison3() { + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } - public void testChainedComparison4() { // PY-5623 - doInspectionTest("ChainedComparison4.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + // PY-5623 + public void testChainedComparison4() { + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } - public void testChainedComparison5() { // PY-6467 - doInspectionTest("ChainedComparison5.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + // PY-6467 + public void testChainedComparison5() { + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } // PY-14002 public void testChainedComparisonWithCommonBinaryExpression() { - doInspectionTest("ChainedComparisonWithCommonBinaryExpression.py", PyChainedComparisonsInspection.class, - PyBundle.message("QFIX.chained.comparison"), true, true); + doInspectionTest(PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true); } - public void testStatementEffect() { // PY-1362, PY-2585 - doInspectionTest("StatementEffect.py", PyStatementEffectInspection.class, - PyBundle.message("QFIX.statement.effect"), true, true); + // PY-1362, PY-2585 + public void testStatementEffect() { + doInspectionTest(PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, true); } - public void testStatementEffectIntroduceVariable() { // PY-1265 - doInspectionTest("StatementEffectIntroduceVariable.py", PyStatementEffectInspection.class, - PyBundle.message("QFIX.statement.effect.introduce.variable"), true, true); + // PY-1265 + public void testStatementEffectIntroduceVariable() { + doInspectionTest(PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect.introduce.variable"), true, true); } - public void testUnresolvedWith() { // PY-2083 + // PY-2083 + public void testUnresolvedWith() { runWithLanguageLevel(LanguageLevel.PYTHON25, new Runnable() { @Override public void run() { - doInspectionTest("UnresolvedWith.py", PyUnresolvedReferencesInspection.class, - PyBundle.message("QFIX.unresolved.reference.add.future"), true, true); + doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.unresolved.reference.add.future"), true, true); } }); } - public void testUnresolvedRefCreateFunction() { // PY-2092 - doInspectionTest("UnresolvedRefCreateFunction.py", PyUnresolvedReferencesInspection.class, + // PY-2092 + public void testUnresolvedRefCreateFunction() { + doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.unresolved.reference.create.function.$0", "ref"), true, true); } @@ -305,59 +308,55 @@ public class PyQuickFixTest extends PyTestCase { myFixture.enableInspections(PyUnresolvedReferencesInspection.class); myFixture.configureByFile("UnresolvedRefNoCreateFunction.py"); myFixture.checkHighlighting(true, false, false); - final IntentionAction intentionAction = myFixture.getAvailableIntention( - PyBundle.message("QFIX.unresolved.reference.create.function.$0", "ref")); + final IntentionAction intentionAction = myFixture.getAvailableIntention(PyBundle.message("QFIX.unresolved.reference.create.function.$0", "ref")); assertNull(intentionAction); } public void testReplaceNotEqOperator() { - doInspectionTest("ReplaceNotEqOperator.py", PyCompatibilityInspection.class, - PyBundle.message("INTN.replace.noteq.operator"), true, true); + doInspectionTest(PyCompatibilityInspection.class, PyBundle.message("INTN.replace.noteq.operator"), true, true); } public void testListCreation() { - doInspectionTest("ListCreation.py", PyListCreationInspection.class, - PyBundle.message("QFIX.list.creation"), true, true); + doInspectionTest(PyListCreationInspection.class, PyBundle.message("QFIX.list.creation"), true, true); } - public void testConvertSingleQuotedDocstring() { //PY-1445 - doInspectionTest("ConvertSingleQuotedDocstring.py", PySingleQuotedDocstringInspection.class, - PyBundle.message("QFIX.convert.single.quoted.docstring"), true, true); + // PY-1445 + public void testConvertSingleQuotedDocstring() { + doInspectionTest(PySingleQuotedDocstringInspection.class, PyBundle.message("QFIX.convert.single.quoted.docstring"), true, true); } - public void testConvertSingleQuotedDocstringEscape() { //PY-8926 - doInspectionTest("ConvertSingleQuotedDocstringEscape.py", PySingleQuotedDocstringInspection.class, - PyBundle.message("QFIX.convert.single.quoted.docstring"), true, true); + // PY-8926 + public void testConvertSingleQuotedDocstringEscape() { + doInspectionTest(PySingleQuotedDocstringInspection.class, PyBundle.message("QFIX.convert.single.quoted.docstring"), true, true); } - public void testDefaultArgument() { //PY-3127 - doInspectionTest("DefaultArgument.py", PyDefaultArgumentInspection.class, - PyBundle.message("QFIX.default.argument"), true, true); + // PY-3127 + public void testDefaultArgument() { + doInspectionTest(PyDefaultArgumentInspection.class, PyBundle.message("QFIX.default.argument"), true, true); } public void testDefaultArgumentEmptyList() { - doInspectionTest("DefaultArgumentEmptyList.py", PyDefaultArgumentInspection.class, - PyBundle.message("QFIX.default.argument"), true, true); + doInspectionTest(PyDefaultArgumentInspection.class, PyBundle.message("QFIX.default.argument"), true, true); } - public void testPyArgumentEqualDefault() { //PY-3125 - doInspectionTest("ArgumentEqualDefault.py", PyArgumentEqualDefaultInspection.class, - PyBundle.message("QFIX.remove.argument.equal.default"), true, true); + // PY-3125 + public void testArgumentEqualDefault() { + doInspectionTest(PyArgumentEqualDefaultInspection.class, PyBundle.message("QFIX.remove.argument.equal.default"), true, true); } - public void testAddCallSuper() { //PY-3315 - doInspectionTest("AddCallSuper.py", PyMissingConstructorInspection.class, - PyBundle.message("QFIX.add.super"), true, true); + // PY-3315 + public void testAddCallSuper() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } - public void testAddCallSuper1() { //PY-4017 - doInspectionTest("AddCallSuper1.py", PyMissingConstructorInspection.class, - PyBundle.message("QFIX.add.super"), true, true); + // PY-4017 + public void testAddCallSuper1() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } - public void testAddCallSuperPass() { //PY-8654 - doInspectionTest("AddCallSuperPass.py", PyMissingConstructorInspection.class, - PyBundle.message("QFIX.add.super"), true, true); + // PY-8654 + public void testAddCallSuperPass() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } @@ -365,8 +364,7 @@ public class PyQuickFixTest extends PyTestCase { public void testAddCallSuperKeywordOnlyParamInSuperInit() { runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { public void run() { - doInspectionTest("AddCallSuperKeywordOnlyParamInSuperInit.py", PyMissingConstructorInspection.class, - PyBundle.message("QFIX.add.super"), true, true); + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } }); } @@ -375,8 +373,7 @@ public class PyQuickFixTest extends PyTestCase { public void testAddCallSuperKeywordOnlyParamInInit() { runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { public void run() { - doInspectionTest("AddCallSuperKeywordOnlyParamInInit.py", PyMissingConstructorInspection.class, - PyBundle.message("QFIX.add.super"), true, true); + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } }); } @@ -385,8 +382,7 @@ public class PyQuickFixTest extends PyTestCase { public void testAddCallSuperSingleStarParamInSuperInit() { runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { public void run() { - doInspectionTest("AddCallSuperSingleStarParamInSuperInit.py", PyMissingConstructorInspection.class, - PyBundle.message("QFIX.add.super"), true, true); + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } }); } @@ -396,8 +392,7 @@ public class PyQuickFixTest extends PyTestCase { runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { @Override public void run() { - doInspectionTest("AddCallSuperSingleStarParamInSuperInitAndVarargInInit.py", PyMissingConstructorInspection.class, - PyBundle.message("QFIX.add.super"), true, true); + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } }); } @@ -407,60 +402,57 @@ public class PyQuickFixTest extends PyTestCase { runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { @Override public void run() { - doInspectionTest("AddCallSuperTypeAnnotationsPreserved.py", PyMissingConstructorInspection.class, - PyBundle.message("QFIX.add.super"), true, true); + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } }); } - //PY-491, PY-13297 + // PY-491, PY-13297 public void testAddEncoding() { - doInspectionTest("AddEncoding.py", PyMandatoryEncodingInspection.class, - PyBundle.message("QFIX.add.encoding"), true, true); + doInspectionTest(PyMandatoryEncodingInspection.class, PyBundle.message("QFIX.add.encoding"), true, true); } // PY-13297 public void testAddEncodingAtLastLine() { - doInspectionTest("AddEncodingAtLastLine.py", PyMandatoryEncodingInspection.class, - PyBundle.message("QFIX.add.encoding"), true, true); + doInspectionTest(PyMandatoryEncodingInspection.class, PyBundle.message("QFIX.add.encoding"), true, true); } - public void testRemoveDecorator() { //PY-3348 - doInspectionTest("RemoveDecorator.py", PyDecoratorInspection.class, - PyBundle.message("QFIX.remove.decorator"), true, true); + // PY-3348 + public void testRemoveDecorator() { + doInspectionTest(PyDecoratorInspection.class, PyBundle.message("QFIX.remove.decorator"), true, true); } public void testAddParameter() { - doInspectionTest("AddParameter.py", PyUnresolvedReferencesInspection.class, + doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.unresolved.reference.add.param.$0", "test"), true, true); } - public void testRenameUnresolvedReference() { //PY-6595 - doInspectionTest("RenameUnresolvedReference.py", PyUnresolvedReferencesInspection.class, - PyBundle.message("QFIX.rename.unresolved.reference"), true, true); + // PY-6595 + public void testRenameUnresolvedReference() { + doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.rename.unresolved.reference"), true, true); } - public void testSetFunctionToLiteral() { //PY-3120 + // PY-3120 + public void testSetFunctionToLiteral() { runWithLanguageLevel(LanguageLevel.PYTHON27, new Runnable() { @Override public void run() { - doInspectionTest("SetFunctionToLiteral.py", PySetFunctionToLiteralInspection.class, - PyBundle.message("QFIX.replace.function.set.with.literal"), true, true); + doInspectionTest(PySetFunctionToLiteralInspection.class, PyBundle.message("QFIX.replace.function.set.with.literal"), true, true); } }); } public void testDictComprehensionToCall() { - doInspectionTest("DictComprehensionToCall.py", PyCompatibilityInspection.class, - PyBundle.message("INTN.convert.dict.comp.to"), true, true); + doInspectionTest(PyCompatibilityInspection.class, PyBundle.message("INTN.convert.dict.comp.to"), true, true); } - public void testDocstringParams() { //PY-3394 + // PY-3394 + public void testDocstringParams() { PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(myFixture.getModule()); documentationSettings.setFormat(DocStringFormat.EPYTEXT); try { - doInspectionTest("DocstringParams.py", PyDocstringInspection.class, - PyBundle.message("QFIX.docstring.add.$0", "b"), true, true); + doInspectionTest(PyDocstringInspection.class, + PyBundle.message("QFIX.docstring.add.$0", "b"), true, true); } finally { documentationSettings.setFormat(DocStringFormat.PLAIN); @@ -471,20 +463,19 @@ public class PyQuickFixTest extends PyTestCase { PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(myFixture.getModule()); documentationSettings.setFormat(DocStringFormat.EPYTEXT); try { - doInspectionTest("DocstringParams1.py", PyDocstringInspection.class, - PyBundle.message("QFIX.docstring.remove.$0", "c"), true, true); + doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "c"), true, true); } finally { documentationSettings.setFormat(DocStringFormat.PLAIN); } } - public void testDocstringParams2() { //PY-4964 + // PY-4964 + public void testDocstringParams2() { PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(myFixture.getModule()); documentationSettings.setFormat(DocStringFormat.EPYTEXT); try { - doInspectionTest("DocstringParams2.py", PyDocstringInspection.class, - PyBundle.message("QFIX.docstring.add.$0", "ham"), true, true); + doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.add.$0", "ham"), true, true); } finally { documentationSettings.setFormat(DocStringFormat.PLAIN); @@ -502,9 +493,9 @@ public class PyQuickFixTest extends PyTestCase { myFixture.checkResultByFile(graftBeforeExt(testFiles[0], "_after")); } - public void testUnresolvedRefTrueFalse() { //PY-3051 - doInspectionTest("UnresolvedRefTrueFalse.py", PyUnresolvedReferencesInspection.class, - PyBundle.message("QFIX.unresolved.reference.replace.$0", "True"), true, true); + // PY-3051 + public void testUnresolvedRefTrueFalse() { + doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.unresolved.reference.replace.$0", "True"), true, true); } public void testUnnecessaryBackslashInArgumentList() { @@ -585,9 +576,16 @@ public class PyQuickFixTest extends PyTestCase { return PythonTestUtil.getTestDataPath() + "/inspections/"; } - protected void doInspectionTest(@TestDataFile @NonNls String testFileName, - final Class inspectionClass, - @NonNls String quickFixName, + private void doInspectionTest(@NotNull Class inspectionClass, + @NotNull String quickFixName, + boolean applyFix, + boolean available) { + doInspectionTest(getTestName(false) + ".py", inspectionClass, quickFixName, applyFix, available); + } + + protected void doInspectionTest(@TestDataFile @NonNls @NotNull String testFileName, + @NotNull Class inspectionClass, + @NonNls @NotNull String quickFixName, boolean applyFix, boolean available) { doInspectionTest(new String[]{testFileName}, inspectionClass, quickFixName, applyFix, available); @@ -603,9 +601,9 @@ public class PyQuickFixTest extends PyTestCase { * @param available true if the fix should be available, false if it should be explicitly not available. * @throws Exception */ - protected void doInspectionTest(@NonNls String[] testFiles, - final Class inspectionClass, - @NonNls String quickFixName, + protected void doInspectionTest(@NonNls @NotNull String[] testFiles, + @NotNull Class inspectionClass, + @NonNls @NotNull String quickFixName, boolean applyFix, boolean available) { myFixture.enableInspections(inspectionClass); From 9f6a7e6d073d4eedd4466df1b8e964ad41d25f6b Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 18 May 2015 12:48:04 +0300 Subject: [PATCH 06/13] PY-15867 Move Python 3 specific tests from PyQuickFixTest to Py3QuickFixTest --- .../com/jetbrains/python/Py3QuickFixTest.java | 75 ++++++++++++++++++ .../com/jetbrains/python/PyQuickFixTest.java | 76 ------------------- 2 files changed, 75 insertions(+), 76 deletions(-) diff --git a/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java b/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java index 6d7389607e5c..29108fe597c9 100644 --- a/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java @@ -73,6 +73,81 @@ public class Py3QuickFixTest extends PyTestCase { }); } + // PY-15867 + public void testAddCallSuperKeywordOnlyParamInSuperInit() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + public void run() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + }); + } + + // PY-15867 + public void testAddCallSuperKeywordOnlyParamInInit() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + public void run() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + }); + } + + // PY-15867 + public void testAddCallSuperSingleStarParamInSuperInit() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + public void run() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + }); + } + + // PY-15867 + public void testAddCallSuperSingleStarParamInSuperInitAndVarargInInit() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + @Override + public void run() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + }); + } + + // PY-15867 + public void testAddCallSuperTypeAnnotationsPreserved() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + @Override + public void run() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + }); + } + + // PY-8991 + public void testRemoveUnicodePrefixFromGluedStringNodesWithSlash() { + runWithLanguageLevel(LanguageLevel.PYTHON32, new Runnable() { + public void run() { + myFixture.configureByFile(getTestName(false) + ".py"); + myFixture.checkHighlighting(true, false, false); + final IntentionAction intentionAction = myFixture.findSingleIntention(PyBundle.message("INTN.remove.leading.$0", "U")); + assertNotNull(intentionAction); + myFixture.launchAction(intentionAction); + myFixture.checkResultByFile(getTestName(false) + "_after.py"); + } + }); + } + + // PY-8990 + public void testRemoveUnicodePrefixFromGluedStringNodesInParenthesis() { + runWithLanguageLevel(LanguageLevel.PYTHON32, new Runnable() { + public void run() { + myFixture.configureByFile(getTestName(false) + ".py"); + myFixture.checkHighlighting(true, false, false); + final IntentionAction intentionAction = myFixture.findSingleIntention(PyBundle.message("INTN.remove.leading.$0", "U")); + assertNotNull(intentionAction); + myFixture.launchAction(intentionAction); + myFixture.checkResultByFile(getTestName(false) + "_after.py"); + } + }); + } + @Override @NonNls protected String getTestDataPath() { diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 05f8258b0e68..7b9c18c5e9f3 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -359,54 +359,6 @@ public class PyQuickFixTest extends PyTestCase { doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } - - // PY-15867 - public void testAddCallSuperKeywordOnlyParamInSuperInit() { - runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { - public void run() { - doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); - } - }); - } - - // PY-15867 - public void testAddCallSuperKeywordOnlyParamInInit() { - runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { - public void run() { - doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); - } - }); - } - - // PY-15867 - public void testAddCallSuperSingleStarParamInSuperInit() { - runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { - public void run() { - doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); - } - }); - } - - // PY-15867 - public void testAddCallSuperSingleStarParamInSuperInitAndVarargInInit() { - runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { - @Override - public void run() { - doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); - } - }); - } - - // PY-15867 - public void testAddCallSuperTypeAnnotationsPreserved() { - runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { - @Override - public void run() { - doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); - } - }); - } - // PY-491, PY-13297 public void testAddEncoding() { doInspectionTest(PyMandatoryEncodingInspection.class, PyBundle.message("QFIX.add.encoding"), true, true); @@ -542,34 +494,6 @@ public class PyQuickFixTest extends PyTestCase { myFixture.checkHighlighting(true, false, true); } - // PY-8991 - public void testRemoveUnicodePrefixFromGluedStringNodesWithSlash() { - runWithLanguageLevel(LanguageLevel.PYTHON32, new Runnable() { - public void run() { - myFixture.configureByFile(getTestName(false) + ".py"); - myFixture.checkHighlighting(true, false, false); - final IntentionAction intentionAction = myFixture.findSingleIntention(PyBundle.message("INTN.remove.leading.$0", "U")); - assertNotNull(intentionAction); - myFixture.launchAction(intentionAction); - myFixture.checkResultByFile(getTestName(false) + "_after.py"); - } - }); - } - - // PY-8990 - public void testRemoveUnicodePrefixFromGluedStringNodesInParenthesis() { - runWithLanguageLevel(LanguageLevel.PYTHON32, new Runnable() { - public void run() { - myFixture.configureByFile(getTestName(false) + ".py"); - myFixture.checkHighlighting(true, false, false); - final IntentionAction intentionAction = myFixture.findSingleIntention(PyBundle.message("INTN.remove.leading.$0", "U")); - assertNotNull(intentionAction); - myFixture.launchAction(intentionAction); - myFixture.checkResultByFile(getTestName(false) + "_after.py"); - } - }); - } - @Override @NonNls protected String getTestDataPath() { From 225dfb37d1de9d62e01387b60101d0ef76c82f8f Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 18 May 2015 15:31:52 +0300 Subject: [PATCH 07/13] PY-15867 Handle name collision between optional and required parameters --- .../quickfix/AddCallSuperQuickFix.java | 63 +++++++++++-------- ...rOptionalAndRequiredParamsNameCollision.py | 8 +++ ...nalAndRequiredParamsNameCollision_after.py | 8 +++ .../com/jetbrains/python/PyQuickFixTest.java | 5 ++ 4 files changed, 57 insertions(+), 27 deletions(-) create mode 100644 python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision.py create mode 100644 python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision_after.py diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java index 193104bdd3bb..ad30ed956acb 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java @@ -18,7 +18,6 @@ package com.jetbrains.python.inspections.quickfix; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.util.PsiTreeUtil; @@ -30,8 +29,7 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.*; /** * For: @@ -145,7 +143,7 @@ public class AddCallSuperQuickFix implements LocalQuickFix { newFunctionParams.add(param.getText()); } for (PyParameter param : superInfo.getRequiredParameters()) { - if (!origInfo.containsRequiredParam(param.getName())) { + if (!origInfo.getAllParameterNames().contains(param.getName())) { newFunctionParams.add(param.getText()); } superCallArgs.add(param.getName()); @@ -182,7 +180,7 @@ public class AddCallSuperQuickFix implements LocalQuickFix { newFunctionParams.add(param.getText()); } for (PyParameter param : superInfo.getRequiredKeywordOnlyParameters()) { - if (!origInfo.containsRequiredKeywordOnlyParameter(param.getName())) { + if (!origInfo.getAllParameterNames().contains(param.getName())) { newFunctionParams.add(param.getText()); } superCallArgs.add(param.getName() + "=" + param.getName()); @@ -242,6 +240,8 @@ public class AddCallSuperQuickFix implements LocalQuickFix { */ private final PyParameter myKeywordContainerParam; + private final Set myAllParameterNames = new LinkedHashSet(); + public ParametersInfo(@NotNull PyParameterList parameterList) { PyParameter positionalContainer = null; PyParameter singleStarParam = null; @@ -249,6 +249,8 @@ public class AddCallSuperQuickFix implements LocalQuickFix { PyParameter selfParam = null; for (PyParameter param : parameterList.getParameters()) { + myAllParameterNames.addAll(collectParameterNames(param)); + if (param.isSelf()) { selfParam = param; } @@ -285,24 +287,6 @@ public class AddCallSuperQuickFix implements LocalQuickFix { myKeywordContainerParam = keywordContainer; } - public boolean containsRequiredParam(@Nullable final String name) { - return ContainerUtil.exists(myRequiredParams, new Condition() { - @Override - public boolean value(PyParameter parameter) { - return name != null && name.equals(parameter.getName()); - } - }); - } - - public boolean containsRequiredKeywordOnlyParameter(@Nullable final String name) { - return ContainerUtil.exists(myRequiredKwOnlyParams, new Condition() { - @Override - public boolean value(PyParameter parameter) { - return name != null && name.equals(parameter.getName()); - } - }); - } - @Nullable public PyParameter getSelfParameter() { return mySelfParam; @@ -310,12 +294,12 @@ public class AddCallSuperQuickFix implements LocalQuickFix { @NotNull public List getRequiredParameters() { - return myRequiredParams; + return Collections.unmodifiableList(myRequiredParams); } @NotNull public List getOptionalParameters() { - return myOptionalParams; + return Collections.unmodifiableList(myOptionalParams); } @Nullable @@ -330,17 +314,42 @@ public class AddCallSuperQuickFix implements LocalQuickFix { @NotNull public List getRequiredKeywordOnlyParameters() { - return myRequiredKwOnlyParams; + return Collections.unmodifiableList(myRequiredKwOnlyParams); } @NotNull public List getOptionalKeywordOnlyParameters() { - return myOptionalKwOnlyParams; + return Collections.unmodifiableList(myOptionalKwOnlyParams); } @Nullable public PyParameter getKeywordContainerParameter() { return myKeywordContainerParam; } + + @NotNull + public Set getAllParameterNames() { + return Collections.unmodifiableSet(myAllParameterNames); + } + } + + @NotNull + private static Set collectParameterNames(@NotNull PyParameter param) { + final LinkedHashSet result = new LinkedHashSet(); + collectParameterNames(param, result); + return Collections.unmodifiableSet(result); + } + + + private static void collectParameterNames(@NotNull PyParameter param, @NotNull Collection acc) { + final PyTupleParameter tupleParam = param.getAsTuple(); + if (tupleParam != null) { + for (PyParameter subParam : tupleParam.getContents()) { + collectParameterNames(subParam, acc); + } + } + else { + ContainerUtil.addIfNotNull(acc, param.getName()); + } } } diff --git a/python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision.py b/python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision.py new file mode 100644 index 000000000000..3d3bb7d4b64e --- /dev/null +++ b/python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision.py @@ -0,0 +1,8 @@ +class A: + def __init__(self, a): + pass + + +class B(A): + def __init__(self, a=1): + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision_after.py b/python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision_after.py new file mode 100644 index 000000000000..1693904e6b58 --- /dev/null +++ b/python/testData/inspections/AddCallSuperOptionalAndRequiredParamsNameCollision_after.py @@ -0,0 +1,8 @@ +class A: + def __init__(self, a): + pass + + +class B(A): + def __init__(self, a=1): + A.__init__(self, a) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 7b9c18c5e9f3..1b791037720b 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -359,6 +359,11 @@ public class PyQuickFixTest extends PyTestCase { doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } + // PY-15867 + public void testAddCallSuperOptionalAndRequiredParamsNameCollision() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + // PY-491, PY-13297 public void testAddEncoding() { doInspectionTest(PyMandatoryEncodingInspection.class, PyBundle.message("QFIX.add.encoding"), true, true); From e307b42af18c424b740c99ba87e177c197ba4aa6 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 18 May 2015 15:56:58 +0300 Subject: [PATCH 08/13] PY-15927 Handle tuple parameters conflicts in "Add call super" quickfix --- .../quickfix/AddCallSuperQuickFix.java | 30 +++++++++++++++---- .../AddCallSuperConflictingTupleParam.py | 7 +++++ ...AddCallSuperConflictingTupleParam_after.py | 7 +++++ .../com/jetbrains/python/PyQuickFixTest.java | 5 ++++ 4 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 python/testData/inspections/AddCallSuperConflictingTupleParam.py create mode 100644 python/testData/inspections/AddCallSuperConflictingTupleParam_after.py diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java index ad30ed956acb..d6568240ecf4 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddCallSuperQuickFix.java @@ -143,10 +143,28 @@ public class AddCallSuperQuickFix implements LocalQuickFix { newFunctionParams.add(param.getText()); } for (PyParameter param : superInfo.getRequiredParameters()) { - if (!origInfo.getAllParameterNames().contains(param.getName())) { - newFunctionParams.add(param.getText()); + // Special case as if base class has constructor __init__((a, b), c) and + // subclass has constructor __init__(a, (b, c)) + final PyTupleParameter tupleParam = param.getAsTuple(); + if (tupleParam != null) { + final List uniqueNames = collectParameterNames(tupleParam); + final boolean hasDuplicates = uniqueNames.removeAll(origInfo.getAllParameterNames()); + if (hasDuplicates) { + newFunctionParams.addAll(uniqueNames); + } + else { + newFunctionParams.add(param.getText()); + } + // Retain original structure of tuple parameter. + // Note that tuple parameters cannot have annotations or nested default values, so it's syntactically safe + superCallArgs.add(param.getText()); + } + else { + if (!origInfo.getAllParameterNames().contains(param.getName())) { + newFunctionParams.add(param.getText()); + } + superCallArgs.add(param.getName()); } - superCallArgs.add(param.getName()); } // Optional parameters (not-keyword) @@ -334,10 +352,10 @@ public class AddCallSuperQuickFix implements LocalQuickFix { } @NotNull - private static Set collectParameterNames(@NotNull PyParameter param) { - final LinkedHashSet result = new LinkedHashSet(); + private static List collectParameterNames(@NotNull PyParameter param) { + final List result = new ArrayList(); collectParameterNames(param, result); - return Collections.unmodifiableSet(result); + return result; } diff --git a/python/testData/inspections/AddCallSuperConflictingTupleParam.py b/python/testData/inspections/AddCallSuperConflictingTupleParam.py new file mode 100644 index 000000000000..276e16dc2e73 --- /dev/null +++ b/python/testData/inspections/AddCallSuperConflictingTupleParam.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, (a, (b, c)), (d, e)): + pass + +class B(A): + def __init__(self, (a, b), c, e): + pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperConflictingTupleParam_after.py b/python/testData/inspections/AddCallSuperConflictingTupleParam_after.py new file mode 100644 index 000000000000..5a41d4a81d09 --- /dev/null +++ b/python/testData/inspections/AddCallSuperConflictingTupleParam_after.py @@ -0,0 +1,7 @@ +class A: + def __init__(self, (a, (b, c)), (d, e)): + pass + +class B(A): + def __init__(self, (a, b), c, e, d): + A.__init__(self, (a, (b, c)), (d, e)) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 1b791037720b..1fc8572ae262 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -364,6 +364,11 @@ public class PyQuickFixTest extends PyTestCase { doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); } + // PY-15927 + public void testAddCallSuperConflictingTupleParam() { + doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); + } + // PY-491, PY-13297 public void testAddEncoding() { doInspectionTest(PyMandatoryEncodingInspection.class, PyBundle.message("QFIX.add.encoding"), true, true); From 9430f1ba424aeddc433a6e86c665cd54deb74fc0 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 18 May 2015 16:07:09 +0300 Subject: [PATCH 09/13] PY-11561 Remove existing test case as it was replaced by more comprehensive one --- .../testData/inspections/AddCallSuperAnnotations.py | 8 -------- .../inspections/AddCallSuperAnnotations_after.py | 8 -------- .../com/jetbrains/python/Py3QuickFixTest.java | 12 +----------- 3 files changed, 1 insertion(+), 27 deletions(-) delete mode 100644 python/testData/inspections/AddCallSuperAnnotations.py delete mode 100644 python/testData/inspections/AddCallSuperAnnotations_after.py diff --git a/python/testData/inspections/AddCallSuperAnnotations.py b/python/testData/inspections/AddCallSuperAnnotations.py deleted file mode 100644 index b50ae1b4fb72..000000000000 --- a/python/testData/inspections/AddCallSuperAnnotations.py +++ /dev/null @@ -1,8 +0,0 @@ -class Example1: - def __init__(self, field1: str): - self.field1 = field1 - - -class Example2(Example1): - def __init__(self): ## Missed call to __init__ of super class - pass \ No newline at end of file diff --git a/python/testData/inspections/AddCallSuperAnnotations_after.py b/python/testData/inspections/AddCallSuperAnnotations_after.py deleted file mode 100644 index ddee178f3ace..000000000000 --- a/python/testData/inspections/AddCallSuperAnnotations_after.py +++ /dev/null @@ -1,8 +0,0 @@ -class Example1: - def __init__(self, field1: str): - self.field1 = field1 - - -class Example2(Example1): - def __init__(self, field1: str): - super().__init__(field1) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java b/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java index 29108fe597c9..10ed5fa597b4 100644 --- a/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/Py3QuickFixTest.java @@ -63,16 +63,6 @@ public class Py3QuickFixTest extends PyTestCase { }); } - // PY-11561 - public void testAddCallSuperAnnotations() { - runWithLanguageLevel(LanguageLevel.PYTHON33, new Runnable() { - @Override - public void run() { - doInspectionTest(PyMissingConstructorInspection.class, PyBundle.message("QFIX.add.super"), true, true); - } - }); - } - // PY-15867 public void testAddCallSuperKeywordOnlyParamInSuperInit() { runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { @@ -110,7 +100,7 @@ public class Py3QuickFixTest extends PyTestCase { }); } - // PY-15867 + // PY-11561 public void testAddCallSuperTypeAnnotationsPreserved() { runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { @Override From 831bcb463a0ed651ee8e06ce9e608bec314e4024 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 18 May 2015 13:26:01 +0200 Subject: [PATCH 10/13] cleanup (prepare to fix IDEA-137908 External documentation (from javadoc.jar) is not shown) --- .../lang/java/JavaDocumentationProvider.java | 20 ++++---- .../PlatformDocumentationUtil.java | 20 ++++---- .../documentation/DocumentationComponent.java | 48 ++++++++++--------- .../ide/actions/ExternalJavaDocAction.java | 10 ++-- .../ide/browsers/BrowserLauncherAppless.java | 6 +-- .../browsers/impl/WebBrowserServiceImpl.java | 2 +- 6 files changed, 55 insertions(+), 51 deletions(-) diff --git a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java index cb215d06380d..8163544fd81a 100644 --- a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java +++ b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -684,7 +684,7 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext } @Nullable - public static List findUrlForClass(PsiClass aClass) { + public static List findUrlForClass(@NotNull PsiClass aClass) { String qName = aClass.getQualifiedName(); if (qName == null) return null; @@ -696,11 +696,11 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext String packageName = ((PsiJavaFile)file).getPackageName(); String relPath; - if (packageName.length() > 0) { - relPath = packageName.replace('.', '/') + '/' + qName.substring(packageName.length() + 1) + HTML_EXTENSION; + if (packageName.isEmpty()) { + relPath = qName + HTML_EXTENSION; } else { - relPath = qName + HTML_EXTENSION; + relPath = packageName.replace('.', '/') + '/' + qName.substring(packageName.length() + 1) + HTML_EXTENSION; } return findUrlForVirtualFile(file.getProject(), virtualFile, relPath); @@ -728,11 +728,11 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext } } - final List orderEntries = fileIndex.getOrderEntriesForFile(virtualFile); - for (OrderEntry orderEntry : orderEntries) { - final String[] files = JavadocOrderRootType.getUrls(orderEntry); - final List httpRoot = PlatformDocumentationUtil.getHttpRoots(files, relPath); - if (httpRoot != null) return httpRoot; + for (OrderEntry orderEntry : fileIndex.getOrderEntriesForFile(virtualFile)) { + List httpRoot = PlatformDocumentationUtil.getHttpRoots(JavadocOrderRootType.getUrls(orderEntry), relPath); + if (httpRoot != null) { + return httpRoot; + } } return null; } diff --git a/platform/core-impl/src/com/intellij/codeInsight/documentation/PlatformDocumentationUtil.java b/platform/core-impl/src/com/intellij/codeInsight/documentation/PlatformDocumentationUtil.java index 966d8173c150..fa519f0249bb 100644 --- a/platform/core-impl/src/com/intellij/codeInsight/documentation/PlatformDocumentationUtil.java +++ b/platform/core-impl/src/com/intellij/codeInsight/documentation/PlatformDocumentationUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -19,10 +19,11 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.ex.http.HttpFileSystem; +import com.intellij.util.SmartList; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; @@ -30,7 +31,6 @@ import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; public class PlatformDocumentationUtil { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.documentation.PlatformDocumentationUtil"); private static final @NonNls Pattern ourLtFixupPattern = Pattern.compile("<([^/^\\w^!])"); @@ -38,19 +38,23 @@ public class PlatformDocumentationUtil { private static final @NonNls String LT_ENTITY = "<"; @Nullable - public static List getHttpRoots(final String[] roots, String relPath) { - final ArrayList result = new ArrayList(); + public static List getHttpRoots(@NotNull String[] roots, String relPath) { + List result = new SmartList(); for (String root : roots) { - final VirtualFile virtualFile = VirtualFileManager.getInstance().findFileByUrl(root); + VirtualFile virtualFile = VirtualFileManager.getInstance().findFileByUrl(root); if (virtualFile != null) { if (virtualFile.getFileSystem() instanceof HttpFileSystem) { String url = virtualFile.getUrl(); - if (!url.endsWith("/")) url += "/"; + if (!url.endsWith("/")) { + url += "/"; + } result.add(url + relPath); } else { VirtualFile file = virtualFile.findFileByRelativePath(relPath); - if (file != null) result.add(file.getUrl()); + if (file != null) { + result.add(file.getUrl()); + } } } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java index d687848a4949..36e8f4005070 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java @@ -705,33 +705,35 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr @Override public void actionPerformed(AnActionEvent e) { - if (myElement != null) { - final PsiElement element = myElement.getElement(); - final DocumentationProvider provider = DocumentationManager.getProviderFromElement(element); - final PsiElement originalElement = DocumentationManager.getOriginalElement(element); - boolean processed = false; - if (provider instanceof CompositeDocumentationProvider) { - for (DocumentationProvider p : ((CompositeDocumentationProvider)provider).getAllProviders()) { - if (p instanceof ExternalDocumentationHandler && ((ExternalDocumentationHandler)p).handleExternal(element, originalElement)) { - processed = true; - break; - } - } - } + if (myElement == null) { + return; + } - if (!processed) { - final Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(e.getDataContext()); - final List urls; - if (!StringUtil.isEmptyOrSpaces(myEffectiveExternalUrl)) { - urls = Collections.singletonList(myEffectiveExternalUrl); - } else { - urls = provider.getUrlFor(element, originalElement); - assert urls != null : provider; - assert !urls.isEmpty() : provider; + final PsiElement element = myElement.getElement(); + final DocumentationProvider provider = DocumentationManager.getProviderFromElement(element); + final PsiElement originalElement = DocumentationManager.getOriginalElement(element); + boolean processed = false; + if (provider instanceof CompositeDocumentationProvider) { + for (DocumentationProvider p : ((CompositeDocumentationProvider)provider).getAllProviders()) { + if (p instanceof ExternalDocumentationHandler && ((ExternalDocumentationHandler)p).handleExternal(element, originalElement)) { + processed = true; + break; } - ExternalJavaDocAction.showExternalJavadoc(urls, component); } } + + if (!processed) { + List urls; + if (!StringUtil.isEmptyOrSpaces(myEffectiveExternalUrl)) { + urls = Collections.singletonList(myEffectiveExternalUrl); + } + else { + urls = provider.getUrlFor(element, originalElement); + assert urls != null : provider; + assert !urls.isEmpty() : provider; + } + ExternalJavaDocAction.showExternalJavadoc(urls, PlatformDataKeys.CONTEXT_COMPONENT.getData(e.getDataContext())); + } } @Override diff --git a/platform/lang-impl/src/com/intellij/ide/actions/ExternalJavaDocAction.java b/platform/lang-impl/src/com/intellij/ide/actions/ExternalJavaDocAction.java index 9d73cd569c40..f974d35afd8d 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/ExternalJavaDocAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/ExternalJavaDocAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -35,11 +35,13 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.util.ArrayUtil; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; -import java.util.HashSet; import java.util.List; +import java.util.Set; public class ExternalJavaDocAction extends AnAction { @@ -90,8 +92,8 @@ public class ExternalJavaDocAction extends AnAction { } } - public static void showExternalJavadoc(List urls, Component component) { - final HashSet set = new HashSet(urls); + public static void showExternalJavadoc(@NotNull List urls, Component component) { + Set set = new THashSet(urls); if (set.size() > 1) { JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep("Choose external documentation root", ArrayUtil.toStringArray(set)) { @Override diff --git a/platform/platform-api/src/com/intellij/ide/browsers/BrowserLauncherAppless.java b/platform/platform-api/src/com/intellij/ide/browsers/BrowserLauncherAppless.java index 2385d0000bee..c7f3989c8b7c 100644 --- a/platform/platform-api/src/com/intellij/ide/browsers/BrowserLauncherAppless.java +++ b/platform/platform-api/src/com/intellij/ide/browsers/BrowserLauncherAppless.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -137,10 +137,6 @@ public class BrowserLauncherAppless extends BrowserLauncher { private void openOrBrowse(@NotNull String url, boolean browse, @Nullable Project project) { url = url.trim(); - if (url.startsWith("jar:")) { - return; - } - URI uri; if (BrowserUtil.isAbsoluteURL(url)) { uri = VfsUtil.toUri(url); diff --git a/xml/impl/src/com/intellij/ide/browsers/impl/WebBrowserServiceImpl.java b/xml/impl/src/com/intellij/ide/browsers/impl/WebBrowserServiceImpl.java index 6cd24659c003..097a1d1859fc 100644 --- a/xml/impl/src/com/intellij/ide/browsers/impl/WebBrowserServiceImpl.java +++ b/xml/impl/src/com/intellij/ide/browsers/impl/WebBrowserServiceImpl.java @@ -48,7 +48,7 @@ public class WebBrowserServiceImpl extends WebBrowserService { VirtualFile file = request.getVirtualFile(); return file instanceof LightVirtualFile || !request.getFile().getViewProvider().isPhysical() ? Collections.emptyList() - : Collections.singleton(Urls.newFromVirtualFile(file)); + : Collections.singletonList(Urls.newFromVirtualFile(file)); } @NotNull From c679615edf09b7084ed639714adc91cbbc417888 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 18 May 2015 15:29:48 +0200 Subject: [PATCH 11/13] IDEA-137908 External documentation (from javadoc.jar) is not shown --- java/java-impl/java-impl.iml | 4 +- .../lang/java/JavaDocumentationProvider.java | 19 ++++++- .../DefaultWebServerRootsProvider.java | 53 ++++++++++++++++++- 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/java/java-impl/java-impl.iml b/java/java-impl/java-impl.iml index cf601bbd0f76..ee71056b6734 100644 --- a/java/java-impl/java-impl.iml +++ b/java/java-impl/java-impl.iml @@ -56,11 +56,11 @@ + - - + \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java index 8163544fd81a..0a61a7c94c06 100644 --- a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java +++ b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java @@ -50,11 +50,14 @@ import com.intellij.psi.util.PsiFormatUtil; import com.intellij.psi.util.PsiFormatUtilBase; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.util.SmartList; +import com.intellij.util.Url; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.builtInWebServer.BuiltInWebBrowserUrlProvider; import java.util.LinkedHashSet; import java.util.List; @@ -707,7 +710,7 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext } @Nullable - public static List findUrlForVirtualFile(final Project project, final VirtualFile virtualFile, final String relPath) { + public static List findUrlForVirtualFile(@NotNull Project project, @NotNull VirtualFile virtualFile, @NotNull String relPath) { final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); Module module = fileIndex.getModuleForFile(virtualFile); if (module == null) { @@ -729,6 +732,20 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext } for (OrderEntry orderEntry : fileIndex.getOrderEntriesForFile(virtualFile)) { + for (VirtualFile root : orderEntry.getFiles(JavadocOrderRootType.getInstance())) { + if (root.getFileSystem() == JarFileSystem.getInstance()) { + VirtualFile file = root.findFileByRelativePath(relPath); + List urls = file == null ? null : BuiltInWebBrowserUrlProvider.getUrls(file, project, null); + if (!ContainerUtil.isEmpty(urls)) { + List result = new SmartList(); + for (Url url : urls) { + result.add(url.toExternalForm()); + } + return result; + } + } + } + List httpRoot = PlatformDocumentationUtil.getHttpRoots(JavadocOrderRootType.getUrls(orderEntry), relPath); if (httpRoot != null) { return httpRoot; diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java b/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java index fa09f74d71ea..e6b6ba3ffc46 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java @@ -33,6 +33,8 @@ import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.PairFunction; import com.intellij.util.PlatformUtils; @@ -207,7 +209,8 @@ final class DefaultWebServerRootsProvider extends WebServerRootsProvider { DirectoryInfo info = directoryIndex.getInfoForFile(file); // we serve excluded files if (!info.isExcluded() && !info.isInProject()) { - return null; + // javadoc jars is "not under project", but actually is, so, let's check project library table + return file.getFileSystem() == JarFileSystem.getInstance() ? getInfoForDocJar(file, project) : null; } VirtualFile root = info.getSourceRoot(); @@ -245,6 +248,54 @@ final class DefaultWebServerRootsProvider extends WebServerRootsProvider { } } + @Nullable + private static PathInfo getInfoForDocJar(@NotNull final VirtualFile file, @NotNull Project project) { + final OrderRootType javaDocRootType = JavadocOrderRootType.getInstance(); + if (javaDocRootType == null) { + return null; + } + + final Ref result = Ref.create(); + Processor processor = new Processor() { + @Override + public boolean process(Library library) { + for (VirtualFile root : library.getFiles(javaDocRootType)) { + if (VfsUtilCore.isAncestor(root, file, true)) { + result.set(new PathInfo(file, root, null, true)); + return false; + } + } + return true; + } + }; + + AccessToken token = ReadAction.start(); + try { + ModuleManager moduleManager = ModuleManager.getInstance(project); + for (Module module : moduleManager.getModules()) { + if (module.isDisposed()) { + continue; + } + + ModuleRootManager.getInstance(module).orderEntries().forEachLibrary(processor); + if (!result.isNull()) { + return result.get(); + } + } + + for (Library library : LibraryTablesRegistrar.getInstance().getLibraryTable(project).getLibraries()) { + if (!processor.process(library)) { + return result.get(); + } + } + } + finally { + token.finish(); + } + + return null; + } + @Nullable private static String getModuleNameQualifier(@NotNull Project project, @Nullable Module module) { if (module != null && From 062d7dbc1a2b0604ea02bfac4c34601838b68356 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 18 May 2015 15:43:05 +0200 Subject: [PATCH 12/13] =?UTF-8?q?IDEA-CR-2749=20must=20not=20be=20strict?= =?UTF-8?q?=20=E2=80=94=20root=20could=20be=20requested?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../builtInWebServer/DefaultWebServerRootsProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java b/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java index e6b6ba3ffc46..5a22e1c7a7df 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java @@ -260,7 +260,7 @@ final class DefaultWebServerRootsProvider extends WebServerRootsProvider { @Override public boolean process(Library library) { for (VirtualFile root : library.getFiles(javaDocRootType)) { - if (VfsUtilCore.isAncestor(root, file, true)) { + if (VfsUtilCore.isAncestor(root, file, false)) { result.set(new PathInfo(file, root, null, true)); return false; } From c113e546f4766abd01beeb04baa31e0eaa6b2103 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 18 May 2015 15:52:04 +0200 Subject: [PATCH 13/13] IDEA-CR-2749 respect module name --- .../DefaultWebServerRootsProvider.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java b/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java index 5a22e1c7a7df..70ed18f5f98b 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.java @@ -255,20 +255,23 @@ final class DefaultWebServerRootsProvider extends WebServerRootsProvider { return null; } - final Ref result = Ref.create(); - Processor processor = new Processor() { + class LibraryProcessor implements Processor { + PathInfo result; + String moduleName; + @Override public boolean process(Library library) { for (VirtualFile root : library.getFiles(javaDocRootType)) { if (VfsUtilCore.isAncestor(root, file, false)) { - result.set(new PathInfo(file, root, null, true)); + result = new PathInfo(file, root, moduleName, true); return false; } } return true; } - }; + } + LibraryProcessor processor = new LibraryProcessor(); AccessToken token = ReadAction.start(); try { ModuleManager moduleManager = ModuleManager.getInstance(project); @@ -277,15 +280,17 @@ final class DefaultWebServerRootsProvider extends WebServerRootsProvider { continue; } + processor.moduleName = module.getName(); ModuleRootManager.getInstance(module).orderEntries().forEachLibrary(processor); - if (!result.isNull()) { - return result.get(); + if (processor.result != null) { + return processor.result; } } + processor.moduleName = null; for (Library library : LibraryTablesRegistrar.getInstance().getLibraryTable(project).getLibraries()) { if (!processor.process(library)) { - return result.get(); + return processor.result; } } }