From ee323fe9d3791a7d8b01a00ff465eea2005c3f3d Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 13 May 2010 18:13:19 +0400 Subject: [PATCH 1/9] if line comment prefix is followed by a single space, uncomment line action deletes that space (PY-980) --- .../editing/uncommentWithSpace.after.py | 1 + .../editing/uncommentWithSpace.before.py | 1 + .../com/jetbrains/python/PyEditingTest.java | 17 +++++++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 python/testData/editing/uncommentWithSpace.after.py create mode 100644 python/testData/editing/uncommentWithSpace.before.py diff --git a/python/testData/editing/uncommentWithSpace.after.py b/python/testData/editing/uncommentWithSpace.after.py new file mode 100644 index 000000000000..b717fffa31d3 --- /dev/null +++ b/python/testData/editing/uncommentWithSpace.after.py @@ -0,0 +1 @@ +this is commented \ No newline at end of file diff --git a/python/testData/editing/uncommentWithSpace.before.py b/python/testData/editing/uncommentWithSpace.before.py new file mode 100644 index 000000000000..d11995642ebc --- /dev/null +++ b/python/testData/editing/uncommentWithSpace.before.py @@ -0,0 +1 @@ +# this is commented \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyEditingTest.java b/python/testSrc/com/jetbrains/python/PyEditingTest.java index 930af0232c74..6c7303e581a2 100644 --- a/python/testSrc/com/jetbrains/python/PyEditingTest.java +++ b/python/testSrc/com/jetbrains/python/PyEditingTest.java @@ -1,5 +1,9 @@ package com.jetbrains.python; +import com.intellij.codeInsight.generation.actions.CommentByLineCommentAction; +import com.intellij.ide.DataManager; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.IdeActions; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; @@ -81,6 +85,19 @@ public class PyEditingTest extends PyLightFixtureTestCase { myFixture.checkResultByFile("/editing/smartUnindent.after.py", true); } + public void testUncommentWithSpace() throws Exception { // PY-980 + myFixture.configureByFile("/editing/uncommentWithSpace.before.py"); + myFixture.getEditor().getCaretModel().moveToLogicalPosition(new LogicalPosition(0, 1)); + CommandProcessor.getInstance().executeCommand(myFixture.getProject(), new Runnable() { + public void run() { + CommentByLineCommentAction action = new CommentByLineCommentAction(); + action.actionPerformed(new AnActionEvent(null, DataManager.getInstance().getDataContext(), "", action.getTemplatePresentation(), + ActionManager.getInstance(), 0)); + } + }, "", null); + myFixture.checkResultByFile("/editing/uncommentWithSpace.after.py", true); + } + private String doTestTyping(final String text, final int offset, final char character) { final PsiFile file = ApplicationManager.getApplication().runWriteAction(new Computable() { public PsiFile compute() { From 04875f58302c9b02584a4826ffe41286b8e08697 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 13 May 2010 18:23:50 +0400 Subject: [PATCH 2/9] import render_to_response when creating a view method if not already imported --- .../src/com/jetbrains/python/actions/AddImportHelper.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python/src/com/jetbrains/python/actions/AddImportHelper.java b/python/src/com/jetbrains/python/actions/AddImportHelper.java index 79bd7a8679a1..083555687202 100644 --- a/python/src/com/jetbrains/python/actions/AddImportHelper.java +++ b/python/src/com/jetbrains/python/actions/AddImportHelper.java @@ -15,10 +15,12 @@ import org.jetbrains.annotations.Nullable; * User: dcheryasov * Date: Apr 24, 2009 3:17:59 AM */ -// intentional package level access -class AddImportHelper { +public class AddImportHelper { private static final Logger LOG = Logger.getInstance("#" + AddImportHelper.class.getName()); + private AddImportHelper() { + } + private static PsiElement getInsertPosition(final PsiFile file) { PsiElement feeler = file.getFirstChild(); LOG.assertTrue(feeler != null); From dab60f2ddf026bbed88b6dd29fb4b3d1283478ec Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 13 May 2010 20:08:46 +0400 Subject: [PATCH 3/9] don't fold import statements if there's just one of them --- python/src/com/jetbrains/python/PythonFoldingBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/PythonFoldingBuilder.java b/python/src/com/jetbrains/python/PythonFoldingBuilder.java index ea4224a9dbf0..2f500f09a0a5 100644 --- a/python/src/com/jetbrains/python/PythonFoldingBuilder.java +++ b/python/src/com/jetbrains/python/PythonFoldingBuilder.java @@ -41,7 +41,7 @@ public class PythonFoldingBuilder implements FoldingBuilder, DumbAware { while (lastImport.getElementType() == TokenType.WHITE_SPACE) { lastImport = lastImport.getTreePrev(); } - if (isImport(lastImport, false)) { + if (isImport(lastImport, false) && firstImport != lastImport) { descriptors.add(new FoldingDescriptor(firstImport, new TextRange(firstImport.getStartOffset(), lastImport.getTextRange().getEndOffset()))); } From 700ee42570d6da477c162629d6c75dd25d00a92c Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 13 May 2010 20:16:51 +0400 Subject: [PATCH 4/9] honor "blank lines after imports" option in Py formatter (PY-987) --- .../com/jetbrains/python/formatter/PyBlock.java | 15 ++++++++++----- .../testData/formatter/blankLineAfterImports.py | 2 ++ .../formatter/blankLineAfterImports_after.py | 3 +++ .../com/jetbrains/python/PyFormatterTest.java | 4 ++++ 4 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 python/testData/formatter/blankLineAfterImports.py create mode 100644 python/testData/formatter/blankLineAfterImports_after.py diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index e60456ebddf0..f78ba293b87d 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -199,13 +199,13 @@ public class PyBlock implements ASTBlock { IElementType type2 = childNode2.getElementType(); if (type1 == PyElementTypes.CLASS_DECLARATION) { - int blankLines = mySettings.BLANK_LINES_AROUND_CLASS + 1; - return Spacing.createSpacing(0, 0, blankLines, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_DECLARATIONS); + return getBlankLinesForOption(mySettings.BLANK_LINES_AROUND_CLASS); } - if (type1 == PyElementTypes.FUNCTION_DECLARATION || (type2 == PyElementTypes.FUNCTION_DECLARATION && type1 == PyElementTypes.CLASS_DECLARATION)) { - int blankLines = mySettings.BLANK_LINES_AROUND_METHOD + 1; - return Spacing.createSpacing(0, 0, blankLines, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_DECLARATIONS); + return getBlankLinesForOption(mySettings.BLANK_LINES_AROUND_METHOD); + } + if (type1 == PyElementTypes.IMPORT_STATEMENT && isStatementOrDeclaration(type2)) { + return getBlankLinesForOption(mySettings.BLANK_LINES_AFTER_IMPORTS); } if (isStatementOrDeclaration(type1) && isStatementOrDeclaration(type2)) { @@ -307,6 +307,11 @@ public class PyBlock implements ASTBlock { return mySettings.getCustomSettings(PyCodeStyleSettings.class); } + private Spacing getBlankLinesForOption(final int option) { + int blankLines = option + 1; + return Spacing.createSpacing(0, 0, blankLines, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_DECLARATIONS); + } + private Spacing getSpacingForOption(boolean isOptionSet) { int spaces = isOptionSet ? 1 : 0; return Spacing.createSpacing(spaces, spaces, 0, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); diff --git a/python/testData/formatter/blankLineAfterImports.py b/python/testData/formatter/blankLineAfterImports.py new file mode 100644 index 000000000000..4c6660866d8c --- /dev/null +++ b/python/testData/formatter/blankLineAfterImports.py @@ -0,0 +1,2 @@ +import sys +def foo(): print sys.platform \ No newline at end of file diff --git a/python/testData/formatter/blankLineAfterImports_after.py b/python/testData/formatter/blankLineAfterImports_after.py new file mode 100644 index 000000000000..461b068fc176 --- /dev/null +++ b/python/testData/formatter/blankLineAfterImports_after.py @@ -0,0 +1,3 @@ +import sys + +def foo(): print sys.platform \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index d867de82c811..ee90fbcda117 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -36,6 +36,10 @@ public class PyFormatterTest extends PyLightFixtureTestCase { doTest(); } + public void testBlankLineAfterImports() throws Exception { + doTest(); + } + private void doTest() throws Exception { myFixture.configureByFile("formatter/" + getTestName(true) + ".py"); ApplicationManager.getApplication().runWriteAction(new Runnable() { From 5f6952e6955a9faefec001b3cb202c17498b327b Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 13 May 2010 20:18:07 +0400 Subject: [PATCH 5/9] honor "blank lines after imports" option in Py formatter (PY-987) --- python/src/com/jetbrains/python/formatter/PyBlock.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index f78ba293b87d..17aa9406fc4d 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -204,7 +204,7 @@ public class PyBlock implements ASTBlock { if (type1 == PyElementTypes.FUNCTION_DECLARATION || (type2 == PyElementTypes.FUNCTION_DECLARATION && type1 == PyElementTypes.CLASS_DECLARATION)) { return getBlankLinesForOption(mySettings.BLANK_LINES_AROUND_METHOD); } - if (type1 == PyElementTypes.IMPORT_STATEMENT && isStatementOrDeclaration(type2)) { + if ((type1 == PyElementTypes.IMPORT_STATEMENT || type1 == PyElementTypes.FROM_IMPORT_STATEMENT) && isStatementOrDeclaration(type2)) { return getBlankLinesForOption(mySettings.BLANK_LINES_AFTER_IMPORTS); } From 1b335b4a1663796d78cab54230474b1fa7d95497 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 13 May 2010 20:19:43 +0400 Subject: [PATCH 6/9] honor "blank lines after imports" option in Py formatter (PY-987) --- python/src/com/jetbrains/python/formatter/PyBlock.java | 6 +++++- python/testData/formatter/blankLineAfterImports.py | 1 + python/testData/formatter/blankLineAfterImports_after.py | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index 17aa9406fc4d..1e350176c996 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -204,7 +204,7 @@ public class PyBlock implements ASTBlock { if (type1 == PyElementTypes.FUNCTION_DECLARATION || (type2 == PyElementTypes.FUNCTION_DECLARATION && type1 == PyElementTypes.CLASS_DECLARATION)) { return getBlankLinesForOption(mySettings.BLANK_LINES_AROUND_METHOD); } - if ((type1 == PyElementTypes.IMPORT_STATEMENT || type1 == PyElementTypes.FROM_IMPORT_STATEMENT) && isStatementOrDeclaration(type2)) { + if (isImportStatement(type1) && (isStatementOrDeclaration(type2) && !isImportStatement(type2))) { return getBlankLinesForOption(mySettings.BLANK_LINES_AFTER_IMPORTS); } @@ -299,6 +299,10 @@ public class PyBlock implements ASTBlock { return null; } + private boolean isImportStatement(IElementType type1) { + return (type1 == PyElementTypes.IMPORT_STATEMENT || type1 == PyElementTypes.FROM_IMPORT_STATEMENT); + } + private static boolean isAround(IElementType type1, IElementType type2, final TokenSet tokenSet) { return tokenSet.contains(type1) || tokenSet.contains(type2); } diff --git a/python/testData/formatter/blankLineAfterImports.py b/python/testData/formatter/blankLineAfterImports.py index 4c6660866d8c..21185c040324 100644 --- a/python/testData/formatter/blankLineAfterImports.py +++ b/python/testData/formatter/blankLineAfterImports.py @@ -1,2 +1,3 @@ import sys +from os import * def foo(): print sys.platform \ No newline at end of file diff --git a/python/testData/formatter/blankLineAfterImports_after.py b/python/testData/formatter/blankLineAfterImports_after.py index 461b068fc176..aea90157f737 100644 --- a/python/testData/formatter/blankLineAfterImports_after.py +++ b/python/testData/formatter/blankLineAfterImports_after.py @@ -1,3 +1,4 @@ import sys +from os import * def foo(): print sys.platform \ No newline at end of file From b4b09995ffaebb3b19c663c78b24ce66890c6cdb Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 13 May 2010 20:51:53 +0400 Subject: [PATCH 7/9] introduce variable: fix broken formatting, add test, set explicit caret position after refactoring, cleanup --- .../introduce/IntroduceHandler.java | 42 ++++++++++--------- .../field/FieldIntroduceHandler.java | 4 +- .../variable/VariableIntroduceHandler.java | 3 +- .../introduceVariable/simple.after.py | 3 ++ .../refactoring/introduceVariable/simple.py | 3 ++ .../jetbrains/python/PythonAllTestsSuite.java | 8 ++-- .../refactoring/PyIntroduceVariableTest.java | 21 ++++++++++ 7 files changed, 56 insertions(+), 28 deletions(-) create mode 100644 python/testData/refactoring/introduceVariable/simple.after.py create mode 100644 python/testData/refactoring/introduceVariable/simple.py create mode 100644 python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java diff --git a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java index 436c5878573e..7f2108804c43 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java @@ -1,5 +1,6 @@ package com.jetbrains.python.refactoring.introduce; +import com.intellij.codeInsight.CodeInsightUtilBase; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; @@ -39,7 +40,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { PyExpressionStatement statement = PsiTreeUtil.getParentOfType(expression, PyExpressionStatement.class); if (statement != null) { if (statement.getExpression() == expression) { - expression.delete(); + statement.delete(); return; } } @@ -85,7 +86,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { return ArrayUtil.toStringArray(res); } - protected void performAction(@NotNull final Project project, Editor editor, PsiFile file, String name, boolean replaceAll, boolean hasConstructor) { + public void performAction(@NotNull final Project project, Editor editor, PsiFile file, String name, boolean replaceAll, boolean hasConstructor) { if (!CommonRefactoringUtil.checkReadOnlyStatus(file)) { return; } @@ -145,10 +146,13 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { initInConstructor = dialog.initInConstructor(); } String assignmentText = name + " = " + expression.getText(); - final PyAssignmentStatement declaration = createDeclaration(project, assignmentText); + PyAssignmentStatement declaration = createDeclaration(project, assignmentText); assert name != null; - performReplace(project, declaration, expression, occurrences, name, replaceAll, initInConstructor); + declaration = performReplace(project, declaration, expression, occurrences, name, replaceAll, initInConstructor); + declaration = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(declaration); + editor.getCaretModel().moveToOffset(declaration.getTextRange().getEndOffset()); + editor.getSelectionModel().removeSelection(); } protected abstract String getHelpId(); @@ -172,24 +176,24 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { return PyRefactoringUtil.getOccurences(expression, context); } - private void performReplace(@NotNull final Project project, - @NotNull final PyAssignmentStatement declaration, - @NotNull final PsiElement expression, - @NotNull final List occurrences, - @NotNull final String name, - final boolean replaceAll, - final boolean initInConstructor) { - new WriteCommandAction(project, expression.getContainingFile()) { - protected void run(final Result result) throws Throwable { + private PyAssignmentStatement performReplace(@NotNull final Project project, + @NotNull final PyAssignmentStatement declaration, + @NotNull final PsiElement expression, + @NotNull final List occurrences, + @NotNull final String name, + final boolean replaceAll, + final boolean initInConstructor) { + return new WriteCommandAction(project, expression.getContainingFile()) { + protected void run(final Result result) throws Throwable { final Pair data = expression.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE); if (data == null) { - addDeclaration(expression, declaration, occurrences, replaceAll, initInConstructor); + result.setResult((PyAssignmentStatement)addDeclaration(expression, declaration, occurrences, replaceAll, initInConstructor)); } else { - addDeclaration(data.first, declaration, occurrences, replaceAll, initInConstructor); + result.setResult((PyAssignmentStatement)addDeclaration(data.first, declaration, occurrences, replaceAll, initInConstructor)); } - PyExpression newExpression = createExpression(project, name, declaration).getExpression(); + PyExpression newExpression = createExpression(project, name, declaration); if (replaceAll) { for (PsiElement occurrence : occurrences) { @@ -200,11 +204,11 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { replaceExpression(newExpression, project, expression); } } - }.execute(); + }.execute().getResultObject(); } - protected PyExpressionStatement createExpression(Project project, String name, PyAssignmentStatement declaration) { - return PyElementGenerator.getInstance(project).createFromText(PyExpressionStatement.class, name); + protected PyExpression createExpression(Project project, String name, PyAssignmentStatement declaration) { + return PyElementGenerator.getInstance(project).createExpressionFromText(name); } @Nullable diff --git a/python/src/com/jetbrains/python/refactoring/introduce/field/FieldIntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/field/FieldIntroduceHandler.java index 9094a601962f..df640a72b4b4 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/field/FieldIntroduceHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/field/FieldIntroduceHandler.java @@ -60,10 +60,10 @@ public class FieldIntroduceHandler extends IntroduceHandler { } @Override - protected PyExpressionStatement createExpression(Project project, String name, PyAssignmentStatement declaration) { + protected PyExpression createExpression(Project project, String name, PyAssignmentStatement declaration) { final String text = declaration.getText(); final String self_name = text.substring(0, text.indexOf('.')); - return PyElementGenerator.getInstance(project).createFromText(PyExpressionStatement.class, self_name + "." + name); + return PyElementGenerator.getInstance(project).createExpressionFromText(self_name + "." + name); } @Override diff --git a/python/src/com/jetbrains/python/refactoring/introduce/variable/VariableIntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/variable/VariableIntroduceHandler.java index 4265dd07b1b2..2fc58aa9cc80 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/variable/VariableIntroduceHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/variable/VariableIntroduceHandler.java @@ -36,8 +36,7 @@ public class VariableIntroduceHandler extends IntroduceHandler { PsiElement anchor = replaceAll ? findAnchor(occurrences) : PsiTreeUtil.getParentOfType(expression, PyStatement.class); assert anchor != null; final PsiElement parent = anchor.getParent(); - parent.addBefore(declaration, anchor); - return parent.getParent(); + return parent.addBefore(declaration, anchor); } private static PsiElement findAnchor(List occurrences) { diff --git a/python/testData/refactoring/introduceVariable/simple.after.py b/python/testData/refactoring/introduceVariable/simple.after.py new file mode 100644 index 000000000000..1dbbc732f972 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/simple.after.py @@ -0,0 +1,3 @@ +def foo(): + a = 1 + print "fooo" \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/simple.py b/python/testData/refactoring/introduceVariable/simple.py new file mode 100644 index 000000000000..378bd793d8de --- /dev/null +++ b/python/testData/refactoring/introduceVariable/simple.py @@ -0,0 +1,3 @@ +def foo(): + 1 + print "fooo" \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java b/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java index 04353e3721c0..d1e8e9efd707 100644 --- a/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java +++ b/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java @@ -1,9 +1,6 @@ package com.jetbrains.python; -import com.jetbrains.python.refactoring.PyCodeFragmentTest; -import com.jetbrains.python.refactoring.PyExtractMethodTest; -import com.jetbrains.python.refactoring.PyInlineLocalTest; -import com.jetbrains.python.refactoring.PyRenameTest; +import com.jetbrains.python.refactoring.*; import com.jetbrains.python.refactoring.classes.PyExtractSuperclassTest; import com.jetbrains.python.refactoring.classes.PyPullUpTest; import com.jetbrains.python.refactoring.classes.PyPushDownTest; @@ -56,7 +53,8 @@ public class PythonAllTestsSuite { PyCodeFragmentTest.class, PyOptimizeImportsTest.class, PySmartEnterTest.class, - PyStatementMoverTest.class + PyStatementMoverTest.class, + PyIntroduceVariableTest.class }; public static TestSuite suite() { diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java new file mode 100644 index 000000000000..19d6cf72452d --- /dev/null +++ b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java @@ -0,0 +1,21 @@ +package com.jetbrains.python.refactoring; + +import com.jetbrains.python.fixtures.PyLightFixtureTestCase; +import com.jetbrains.python.refactoring.introduce.variable.VariableIntroduceHandler; + +/** + * @author yole + */ +public class PyIntroduceVariableTest extends PyLightFixtureTestCase { + @Override + protected String getTestDataPath() { + return super.getTestDataPath() + "/refactoring/introduceVariable"; + } + + public void testSimple() throws Exception { + myFixture.configureByFile("simple.py"); + VariableIntroduceHandler handler = new VariableIntroduceHandler(); + handler.performAction(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile(), "a", true, false); + myFixture.checkResultByFile("simple.after.py"); + } +} From c0c4b19a8a96bba1afb469d67ab6c58213f3e241 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 13 May 2010 21:54:16 +0400 Subject: [PATCH 8/9] testdata fix --- python/testData/inspections/AddImportDocComment_after.py | 1 + python/testData/inspections/AddImportDoc_after.py | 1 + python/testData/inspections/AddImport_after.py | 1 + 3 files changed, 3 insertions(+) diff --git a/python/testData/inspections/AddImportDocComment_after.py b/python/testData/inspections/AddImportDocComment_after.py index 1e4f16d23e8b..63fdf7f5b0bc 100644 --- a/python/testData/inspections/AddImportDocComment_after.py +++ b/python/testData/inspections/AddImportDocComment_after.py @@ -1,6 +1,7 @@ # Comments here "Doc comment stays intact" import ImportTarget + ImportTarget.foo foo = 1 diff --git a/python/testData/inspections/AddImportDoc_after.py b/python/testData/inspections/AddImportDoc_after.py index 78902e852c5f..bacd9478ec83 100644 --- a/python/testData/inspections/AddImportDoc_after.py +++ b/python/testData/inspections/AddImportDoc_after.py @@ -1,5 +1,6 @@ "Doc comment stays intact" import ImportTarget + ImportTarget.foo foo = 1 diff --git a/python/testData/inspections/AddImport_after.py b/python/testData/inspections/AddImport_after.py index 2669267aa4ea..46273bc4a5bc 100644 --- a/python/testData/inspections/AddImport_after.py +++ b/python/testData/inspections/AddImport_after.py @@ -1,4 +1,5 @@ import ImportTarget + ImportTarget.foo foo = 1 From cd9d9760c9bdef61251eee7b3ad7f88b768689ad Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 13 May 2010 22:35:22 +0400 Subject: [PATCH 9/9] Create Class quickfix (initial) (PY-197) --- .../python/actions/CreateClassQuickFix.java | 52 +++++++++++++++++++ .../PyUnresolvedReferencesInspection.java | 3 ++ python/testData/inspections/AddClass.py | 3 ++ python/testData/inspections/AddClass_after.py | 6 +++ .../com/jetbrains/python/PyQuickFixTest.java | 4 ++ 5 files changed, 68 insertions(+) create mode 100644 python/src/com/jetbrains/python/actions/CreateClassQuickFix.java create mode 100644 python/testData/inspections/AddClass.py create mode 100644 python/testData/inspections/AddClass_after.py diff --git a/python/src/com/jetbrains/python/actions/CreateClassQuickFix.java b/python/src/com/jetbrains/python/actions/CreateClassQuickFix.java new file mode 100644 index 000000000000..d5a97f54ad8f --- /dev/null +++ b/python/src/com/jetbrains/python/actions/CreateClassQuickFix.java @@ -0,0 +1,52 @@ +package com.jetbrains.python.actions; + +import com.intellij.codeInsight.CodeInsightUtilBase; +import com.intellij.codeInsight.template.TemplateBuilder; +import com.intellij.codeInsight.template.TemplateBuilderFactory; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyElementGenerator; +import com.jetbrains.python.psi.PyFile; +import org.jetbrains.annotations.NotNull; + +/** + * @author yole + */ +public class CreateClassQuickFix implements LocalQuickFix { + private final String myClassName; + private final PsiElement myAnchor; + + public CreateClassQuickFix(String className, PsiElement anchor) { + myClassName = className; + myAnchor = anchor; + } + + @NotNull + public String getName() { + return "Create class '" + myClassName + "'"; + } + + @NotNull + public String getFamilyName() { + return "Create Class"; + } + + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiElement anchor = myAnchor; + while(!(anchor.getParent() instanceof PyFile)) { + anchor = anchor.getParent(); + } + PyClass pyClass = PyElementGenerator.getInstance(myAnchor.getProject()).createFromText(PyClass.class, + "class " + myClassName + "(object):\n pass"); + pyClass = (PyClass) anchor.getParent().addBefore(pyClass, anchor); + pyClass = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(pyClass); + TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(pyClass); + builder.replaceElement(pyClass.getSuperClassExpressions() [0], "object"); + builder.replaceElement(pyClass.getStatementList(), "pass"); + builder.run(); + } + +} diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index 4aa7b680c4e1..b944c3451dcd 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -406,6 +406,9 @@ public class PyUnresolvedReferencesInspection extends LocalInspectionTool { if (ref_element != null && ref_is_importable && hint_action == null) { actions.add(new AddImportAction(reference)); } + if (ref_text.length() > 2 && Character.isUpperCase(ref_text.charAt(0)) && !Character.isUpperCase(ref_text.charAt(1))) { + actions.add(new CreateClassQuickFix(ref_text, reference.getElement())); + } } } String description = description_buf.toString(); diff --git a/python/testData/inspections/AddClass.py b/python/testData/inspections/AddClass.py new file mode 100644 index 000000000000..d1e7363de71a --- /dev/null +++ b/python/testData/inspections/AddClass.py @@ -0,0 +1,3 @@ +class XyzzyTest: + def testSimple(self): + c = Xyzzy() diff --git a/python/testData/inspections/AddClass_after.py b/python/testData/inspections/AddClass_after.py new file mode 100644 index 000000000000..2e177c5b5b46 --- /dev/null +++ b/python/testData/inspections/AddClass_after.py @@ -0,0 +1,6 @@ +class Xyzzy(object): + pass + +class XyzzyTest: + def testSimple(self): + c = Xyzzy() diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index bcf1f604ced4..ca540b8b9790 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -140,6 +140,10 @@ public class PyQuickFixTest extends PyLightFixtureTestCase { PyBundle.message("QFIX.replace.equality"), true, true); } + public void testAddClassFix() throws Exception { + doInspectionTest("AddClass.py", PyUnresolvedReferencesInspection.class, "Create class 'Xyzzy'", true, true); + } + @NonNls protected String getTestDataPath() { return PythonTestUtil.getTestDataPath() + "/inspections/";