diff --git a/python/helpers/pydev/pydevd_file_utils.py b/python/helpers/pydev/pydevd_file_utils.py index 576815081082..151f89533239 100644 --- a/python/helpers/pydev/pydevd_file_utils.py +++ b/python/helpers/pydev/pydevd_file_utils.py @@ -47,6 +47,7 @@ import sys import traceback import zipfile +normcase = os.path.normcase basename = os.path.basename exists = os.path.exists join = os.path.join @@ -81,7 +82,7 @@ def _NormFile(filename): try: return NORM_FILENAME_CONTAINER[filename] except KeyError: - r = rPath(filename) + r = normcase(rPath(filename)) #cache it for fast access later NORM_FILENAME_CONTAINER[filename] = r return r @@ -186,7 +187,7 @@ if PATHS_FROM_ECLIPSE_TO_PYTHON: return NORM_FILENAME_TO_SERVER_CONTAINER[filename] except KeyError: #used to translate a path from the client to the debug server - translated = filename + translated = normcase(filename) for eclipse_prefix, server_prefix in PATHS_FROM_ECLIPSE_TO_PYTHON: if translated.startswith(eclipse_prefix): if DEBUG_CLIENT_SERVER_TRANSLATION: diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 3f364de6437d..1111e6f9f635 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -227,6 +227,7 @@ + callback) { + public static PsiElement appendToMethod(PyFunction init, Function callback) { // add this field as the last stmt of the constructor final PyStatementList stmt_list = init.getStatementList(); PyStatement[] stmts = stmt_list.getStatements(); // NOTE: rather wasteful, consider iterable stmt list @@ -93,7 +93,7 @@ public class AddFieldQuickFix implements LocalQuickFix { if (cls != null && item_name != null) { PyFunction init = cls.findMethodByName(PyNames.INIT, false); if (init != null) { - return appendToInit(init, callback); + return appendToMethod(init, callback); } else { // no init! boldly copy ancestor's. for (PyClass ancestor : cls.iterateAncestorClasses()) { @@ -105,7 +105,7 @@ public class AddFieldQuickFix implements LocalQuickFix { return null; } - appendToInit(new_init, callback); + appendToMethod(new_init, callback); PsiElement add_anchor = null; PyFunction[] meths = cls.getMethods(); diff --git a/python/src/com/jetbrains/python/findUsages/PyFindUsagesHandlerFactory.java b/python/src/com/jetbrains/python/findUsages/PyFindUsagesHandlerFactory.java index 4e16c3a7f63c..934cdab8df82 100644 --- a/python/src/com/jetbrains/python/findUsages/PyFindUsagesHandlerFactory.java +++ b/python/src/com/jetbrains/python/findUsages/PyFindUsagesHandlerFactory.java @@ -2,13 +2,19 @@ package com.jetbrains.python.findUsages; import com.intellij.find.findUsages.FindUsagesHandler; import com.intellij.find.findUsages.FindUsagesHandlerFactory; +import com.intellij.openapi.ui.Messages; import com.intellij.psi.PsiElement; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.PyFunction; +import com.jetbrains.python.psi.search.PySuperMethodsSearch; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + /** * @author yole */ @@ -26,8 +32,39 @@ public class PyFindUsagesHandlerFactory extends FindUsagesHandlerFactory { return new PyModuleFindUsagesHandler((PyFile) element); } if (element instanceof PyFunction) { + if (!forHighlightUsages) { + final Collection superMethods = PySuperMethodsSearch.search((PyFunction)element, true).findAll(); + if (superMethods.size() > 0) { + final PsiElement next = superMethods.iterator().next(); + // TODO should do this for Jython functions overriding Java methods too + if (next instanceof PyFunction && !isInClassobj((PyFunction)next)) { + StringBuilder messageBuilder = new StringBuilder("Method "); + messageBuilder.append(((PyFunction)element).getName()); + messageBuilder.append(" overrides method of class "); + messageBuilder.append(((PyFunction)next).getContainingClass().getName()); + messageBuilder.append(".\nDo you want to find usages of the base method?"); + int rc = Messages.showYesNoCancelDialog(element.getProject(), messageBuilder.toString(), "Find Usages", Messages.getQuestionIcon()); + if (rc == 0) { + List allMethods = new ArrayList(); + allMethods.add(element); + allMethods.addAll(superMethods); + return new PyFunctionFindUsagesHandler(element, allMethods); + } + if (rc == 1) { + return new PyFunctionFindUsagesHandler(element); + } + return FindUsagesHandler.NULL_HANDLER; + } + } + + } return new PyFunctionFindUsagesHandler(element); } return new PyClassFindUsagesHandler((PyClass)element); } + + private static boolean isInClassobj(PyFunction fun) { + final PyClass containingClass = fun.getContainingClass(); + return containingClass != null && PyNames.FAKE_OLD_BASE.equals(containingClass.getName()); + } } diff --git a/python/src/com/jetbrains/python/findUsages/PyFunctionFindUsagesHandler.java b/python/src/com/jetbrains/python/findUsages/PyFunctionFindUsagesHandler.java index cc7809ea92d0..0ab8c2c823f1 100644 --- a/python/src/com/jetbrains/python/findUsages/PyFunctionFindUsagesHandler.java +++ b/python/src/com/jetbrains/python/findUsages/PyFunctionFindUsagesHandler.java @@ -4,16 +4,32 @@ import com.intellij.find.findUsages.FindUsagesHandler; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; +import java.util.List; + /** * @author yole */ public class PyFunctionFindUsagesHandler extends FindUsagesHandler { + private final List myAllElements; + protected PyFunctionFindUsagesHandler(@NotNull PsiElement psiElement) { super(psiElement); + myAllElements = null; + } + + protected PyFunctionFindUsagesHandler(@NotNull PsiElement psiElement, List allElements) { + super(psiElement); + myAllElements = allElements; } @Override protected boolean isSearchForTextOccurencesAvailable(PsiElement psiElement, boolean isSingleFile) { return true; } + + @NotNull + @Override + public PsiElement[] getPrimaryElements() { + return myAllElements != null ? myAllElements.toArray(new PsiElement[myAllElements.size()]) : super.getPrimaryElements(); + } } diff --git a/python/src/com/jetbrains/python/findUsages/PyImportFilteringRule.java b/python/src/com/jetbrains/python/findUsages/PyImportFilteringRule.java new file mode 100644 index 000000000000..0e9ba867df64 --- /dev/null +++ b/python/src/com/jetbrains/python/findUsages/PyImportFilteringRule.java @@ -0,0 +1,29 @@ +package com.jetbrains.python.findUsages; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.usages.Usage; +import com.intellij.usages.rules.ImportFilteringRule; +import com.intellij.usages.rules.PsiElementUsage; +import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.psi.PyImportStatementBase; + +/** + * @author yole + */ +public class PyImportFilteringRule extends ImportFilteringRule { + @Override + public boolean isVisible(Usage usage) { + if (usage instanceof PsiElementUsage) { + final PsiElement psiElement = ((PsiElementUsage)usage).getElement(); + final PsiFile containingFile = psiElement.getContainingFile(); + if (containingFile instanceof PyFile) { + // check whether the element is in the import list + final PyImportStatementBase importStatement = PsiTreeUtil.getParentOfType(psiElement, PyImportStatementBase.class, true); + return importStatement == null; + } + } + return true; + } +} diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index c40796b32336..584ba85dad95 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -352,7 +352,7 @@ public class PyBlock implements ASTBlock { if (type2 == PyElementTypes.STATEMENT_LIST) { return Spacing.createSpacing(1, Integer.MAX_VALUE, 0, true, 0); } - if (parentType == PyElementTypes.KEY_VALUE_EXPRESSION) { + if (parentType == PyElementTypes.KEY_VALUE_EXPRESSION || parentType == PyElementTypes.LAMBDA_EXPRESSION) { return getSpacingForOption(getPySettings().SPACE_AFTER_PY_COLON); } } @@ -387,7 +387,7 @@ public class PyBlock implements ASTBlock { if (type2 == PyElementTypes.ARGUMENT_LIST) { return getSpacingForOption(mySettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES); } - if (type2 == PyElementTypes.PARAMETER_LIST) { + if (type2 == PyElementTypes.PARAMETER_LIST && type1 != PyTokenTypes.LAMBDA_KEYWORD) { return getSpacingForOption(mySettings.SPACE_BEFORE_METHOD_PARENTHESES); } diff --git a/python/src/com/jetbrains/python/parsing/FunctionParsing.java b/python/src/com/jetbrains/python/parsing/FunctionParsing.java index 62f4d9508937..05a6ebc3e7ec 100644 --- a/python/src/com/jetbrains/python/parsing/FunctionParsing.java +++ b/python/src/com/jetbrains/python/parsing/FunctionParsing.java @@ -173,11 +173,15 @@ public class FunctionParsing extends Parsing { } } - if (myBuilder.getTokenType() == endToken) { + if (myBuilder.getTokenType() == endToken && endToken == PyTokenTypes.RPAR) { myBuilder.advanceLexer(); } parameterList.done(PyElementTypes.PARAMETER_LIST); + + if (myBuilder.getTokenType() == endToken && endToken == PyTokenTypes.COLON) { + myBuilder.advanceLexer(); + } } private void parseParameterSubList() { diff --git a/python/src/com/jetbrains/python/psi/search/PyInitReferenceSearchExecutor.java b/python/src/com/jetbrains/python/psi/search/PyInitReferenceSearchExecutor.java index 7bcb0c3be475..bfeffac198eb 100644 --- a/python/src/com/jetbrains/python/psi/search/PyInitReferenceSearchExecutor.java +++ b/python/src/com/jetbrains/python/psi/search/PyInitReferenceSearchExecutor.java @@ -1,5 +1,7 @@ package com.jetbrains.python.psi.search; +import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.QueryExecutorBase; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; @@ -23,23 +25,34 @@ public class PyInitReferenceSearchExecutor extends QueryExecutorBase suggestedNames = operation.getSuggestedNames(); + if (suggestedNames.size() > 0) { + operation.setName(suggestedNames.iterator().next()); + } + else { + operation.setName("x"); + } + } + } + public enum InitPlace { SAME_METHOD, CONSTRUCTOR, SET_UP } - private static void replaceExpression(PyExpression newExpression, Project project, PsiElement expression) { + @Nullable + private static PsiElement replaceExpression(PyExpression newExpression, Project project, PsiElement expression) { PyExpressionStatement statement = PsiTreeUtil.getParentOfType(expression, PyExpressionStatement.class); if (statement != null) { if (statement.getExpression() == expression) { statement.delete(); - return; + return null; } } - PyPsiUtils.replaceExpression(project, expression, newExpression); + return PyPsiUtils.replaceExpression(project, expression, newExpression); } private final IntroduceValidator myValidator; @@ -83,7 +98,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { } public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { - performAction(project, editor, file, null, InitPlace.SAME_METHOD, false, false, false); + performAction(new IntroduceOperation(project, editor, file, null, false, false, false)); } public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { @@ -134,16 +149,18 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { } public void performAction(@NotNull final Project project, Editor editor, PsiFile file, String name, boolean replaceAll, boolean hasConstructor, boolean isTestClass) { - performAction(project, editor, file, name, InitPlace.SAME_METHOD, replaceAll, hasConstructor, isTestClass); + performAction(new IntroduceOperation(project, editor, file, name, replaceAll, hasConstructor, isTestClass)); } - public void performAction(@NotNull final Project project, Editor editor, PsiFile file, String name, InitPlace initInConstructor, boolean replaceAll, boolean hasConstructor, boolean isTestClass) { + public void performAction(IntroduceOperation operation) { + final PsiFile file = operation.getFile(); if (!CommonRefactoringUtil.checkReadOnlyStatus(file)) { return; } PsiElement element1 = null; PsiElement element2 = null; + final Editor editor = operation.getEditor(); final SelectionModel selectionModel = editor.getSelectionModel(); boolean singleElementSelection = false; if (selectionModel.hasSelection()) { @@ -162,7 +179,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { } } else { - if (smartIntroduce(file, editor, name, initInConstructor, replaceAll, hasConstructor, isTestClass)) { + if (smartIntroduce(operation)) { return; } final CaretModel caretModel = editor.getCaretModel(); @@ -173,6 +190,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { element2 = file.findElementAt(document.getLineEndOffset(lineNumber) - 1); } } + final Project project = operation.getProject(); if (element1 == null || element2 == null) { showCannotPerformError(project, editor); return; @@ -194,7 +212,8 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { if (!checkIntroduceContext(file, editor, element1)) { return; } - performActionOnElement(editor, element1, name, initInConstructor, replaceAll, hasConstructor, isTestClass); + operation.setElement(element1); + performActionOnElement(operation); } private void showCannotPerformError(Project project, Editor editor) { @@ -202,7 +221,9 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { "refactoring.extractMethod"); } - private boolean smartIntroduce(final PsiFile file, final Editor editor, final String name, final InitPlace initInConstructor, final boolean replaceAll, final boolean hasConstructor, final boolean isTestClass) { + private boolean smartIntroduce(final IntroduceOperation operation) { + final Editor editor = operation.getEditor(); + final PsiFile file = operation.getFile(); int offset = editor.getCaretModel().getOffset(); PsiElement elementAtCaret = file.findElementAt(offset); if (!checkIntroduceContext(file, editor, elementAtCaret)) return true; @@ -217,14 +238,16 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { elementAtCaret = elementAtCaret.getParent(); } if (expressions.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) { - performActionOnElement(editor, expressions.get(0), name, initInConstructor, replaceAll, hasConstructor, isTestClass); + operation.setElement(expressions.get(0)); + performActionOnElement(operation); return true; } else if (expressions.size() > 1) { IntroduceTargetChooser.showChooser(editor, expressions, new Pass() { @Override public void pass(PyExpression pyExpression) { - performActionOnElement(editor, pyExpression, name, initInConstructor, replaceAll, hasConstructor, isTestClass); + operation.setElement(pyExpression); + performActionOnElement(operation); } }, new Function() { public String fun(PyExpression pyExpression) { @@ -261,56 +284,94 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { return true; } - private void performActionOnElement(Editor editor, - @NotNull PsiElement element, - String name, - InitPlace initInConstructor, - boolean replaceAll, - boolean hasConstructor, - boolean isTestClass) { - final Project project = element.getProject(); - if (!checkEnabled(project, editor, element, myDialogTitle)) { + private void performActionOnElement(IntroduceOperation operation) { + final Project project = operation.getProject(); + if (!checkEnabled(project, operation.getEditor(), operation.getElement(), myDialogTitle)) { return; } + final PsiElement element = operation.getElement(); final PsiElement parent = element.getParent(); final PyExpression initializer = parent instanceof PyAssignmentStatement ? ((PyAssignmentStatement)parent).getAssignedValue() : (PyExpression)element; + operation.setInitializer(initializer); - final List occurrences; if (initializer.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE) == null) { - occurrences = getOccurrences(element, initializer); + operation.setOccurrences(getOccurrences(element, initializer)); + } + operation.setSuggestedNames(getSuggestedNames(initializer)); + if (operation.getOccurrences().size() == 0) { + operation.setReplaceAll(false); + } + + performActionOnElementOccurrences(operation); + } + + protected void performActionOnElementOccurrences(final IntroduceOperation operation) { + final Editor editor = operation.getEditor(); + if (editor.getSettings().isVariableInplaceRenameEnabled() && !ApplicationManager.getApplication().isUnitTestMode()) { + ensureName(operation); + new OccurrencesChooser(editor) + .showChooser(operation.getElement(), operation.getOccurrences(), new Pass() { + @Override + public void pass(OccurrencesChooser.ReplaceChoice replaceChoice) { + operation.setReplaceAll(replaceChoice == OccurrencesChooser.ReplaceChoice.ALL); + performInplaceIntroduce(operation); + } + }); } else { - occurrences = Collections.emptyList(); + performIntroduceWithDialog(operation); } - Collection possibleNames = getSuggestedNames(initializer); - replaceAll &= occurrences.size() > 0; + } + + protected void performInplaceIntroduce(IntroduceOperation operation) { + final PyAssignmentStatement statement = performRefactoring(operation); + PyTargetExpression target = (PyTargetExpression) statement.getTargets() [0]; + final List occurrences = operation.getOccurrences(); + final InplaceVariableIntroducer introducer = + new PyInplaceVariableIntroducer(target, operation, occurrences); + introducer.performInplaceRename(false, new LinkedHashSet(operation.getSuggestedNames())); + } - if (name == null) { - PyIntroduceDialog dialog = new PyIntroduceDialog(project, initializer, myDialogTitle, myValidator, occurrences.size(), possibleNames, getHelpId(), hasConstructor, isTestClass); + protected void performIntroduceWithDialog(IntroduceOperation operation) { + final Project project = operation.getProject(); + if (operation.getName() == null) { + PyIntroduceDialog dialog = new PyIntroduceDialog(project, myDialogTitle, myValidator, getHelpId(), operation); dialog.show(); if (!dialog.isOK()) { return; } - name = dialog.getName(); - replaceAll = dialog.doReplaceAllOccurrences(); - initInConstructor = dialog.getInitPlace(); + operation.setName(dialog.getName()); + operation.setReplaceAll(dialog.doReplaceAllOccurrences()); + operation.setInitPlace(dialog.getInitPlace()); } - initInConstructor = initInConstructor != null ? initInConstructor : InitPlace.SAME_METHOD; - String assignmentText = name + " = " + initializer.getText().replace("\n", " "); - PsiElement anchor = replaceAll ? findAnchor(occurrences) : PsiTreeUtil.getParentOfType(initializer, PyStatement.class); - PyAssignmentStatement declaration = createDeclaration(project, assignmentText, anchor); - - assert name != null; - declaration = performReplace(project, element, declaration, initializer, occurrences, name, replaceAll, initInConstructor); - declaration = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(declaration); + PyAssignmentStatement declaration = performRefactoring(operation); + final Editor editor = operation.getEditor(); editor.getCaretModel().moveToOffset(declaration.getTextRange().getEndOffset()); editor.getSelectionModel().removeSelection(); } + protected PyAssignmentStatement performRefactoring(IntroduceOperation operation) { + PyAssignmentStatement declaration = createDeclaration(operation); + + declaration = performReplace(declaration, operation); + declaration = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(declaration); + return declaration; + } + + public PyAssignmentStatement createDeclaration(IntroduceOperation operation) { + final Project project = operation.getProject(); + final PyExpression initializer = operation.getInitializer(); + String assignmentText = operation.getName() + " = " + initializer.getText().replace("\n", " "); + PsiElement anchor = operation.isReplaceAll() + ? findAnchor(operation.getOccurrences()) + : PsiTreeUtil.getParentOfType(initializer, PyStatement.class); + return createDeclaration(project, assignmentText, anchor); + } + protected abstract String getHelpId(); protected PyAssignmentStatement createDeclaration(Project project, String assignmentText, PsiElement anchor) { @@ -333,40 +394,47 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { return PyRefactoringUtil.getOccurrences(expression, context); } - private PyAssignmentStatement performReplace(@NotNull final Project project, - final PsiElement element, - @NotNull final PyAssignmentStatement declaration, - @NotNull final PsiElement expression, - @NotNull final List occurrences, - @NotNull final String name, - final boolean replaceAll, - final InitPlace initInConstructor) { + private PyAssignmentStatement performReplace(@NotNull final PyAssignmentStatement declaration, + final IntroduceOperation operation) { + final PyExpression expression = operation.getInitializer(); + final Project project = operation.getProject(); 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) { - result.setResult((PyAssignmentStatement)addDeclaration(expression, declaration, occurrences, replaceAll, initInConstructor)); - } - else { - result.setResult((PyAssignmentStatement)addDeclaration(data.first, declaration, occurrences, replaceAll, initInConstructor)); - } + result.setResult(addDeclaration(operation, declaration)); - PyExpression newExpression = createExpression(project, name, declaration); + PyExpression newExpression = createExpression(project, operation.getName(), declaration); - if (replaceAll) { - for (PsiElement occurrence : occurrences) { - replaceExpression(newExpression, project, occurrence); + if (operation.isReplaceAll()) { + List newOccurrences = new ArrayList(); + for (PsiElement occurrence : operation.getOccurrences()) { + final PsiElement replaced = replaceExpression(newExpression, project, occurrence); + if (replaced != null) { + newOccurrences.add(replaced); + } } + operation.setOccurrences(newOccurrences); } else { replaceExpression(newExpression, project, expression); } - postRefactoring(element); + postRefactoring(operation.getElement()); } }.execute().getResultObject(); } + @Nullable + public PyAssignmentStatement addDeclaration(IntroduceOperation operation, PyAssignmentStatement declaration) { + final PsiElement expression = operation.getInitializer(); + final Pair data = expression.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE); + if (data == null) { + return (PyAssignmentStatement)addDeclaration(expression, declaration, operation); + } + else { + return (PyAssignmentStatement)addDeclaration(data.first, declaration, operation); + } + } + protected PyExpression createExpression(Project project, String name, PyAssignmentStatement declaration) { return PyElementGenerator.getInstance(project).createExpressionFromText(name); } @@ -374,10 +442,25 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { @Nullable protected abstract PsiElement addDeclaration(@NotNull final PsiElement expression, @NotNull final PsiElement declaration, - @NotNull final List occurrences, - final boolean replaceAll, - final InitPlace initInConstructor); + @NotNull IntroduceOperation operation); protected void postRefactoring(PsiElement element) { } + + private static class PyInplaceVariableIntroducer extends InplaceVariableIntroducer { + private final PyTargetExpression myTarget; + + public PyInplaceVariableIntroducer(PyTargetExpression target, + IntroduceOperation operation, + List occurrences) { + super(target, operation.getEditor(), operation.getProject(), "Introduce Variable", + occurrences.toArray(new PsiElement[occurrences.size()]), null); + myTarget = target; + } + + @Override + protected PsiElement checkLocalScope() { + return myTarget.getContainingFile(); + } + } } diff --git a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceOperation.java b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceOperation.java new file mode 100644 index 000000000000..06b203f8d655 --- /dev/null +++ b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceOperation.java @@ -0,0 +1,122 @@ +package com.jetbrains.python.refactoring.introduce; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.jetbrains.python.psi.PyExpression; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * @author yole + */ +public class IntroduceOperation { + private final Project myProject; + private final Editor myEditor; + private final PsiFile myFile; + private String myName; + private IntroduceHandler.InitPlace myInitPlace = IntroduceHandler.InitPlace.SAME_METHOD; + private boolean myReplaceAll; + private final boolean myHasConstructor; + private final boolean myTestClass; + private PsiElement myElement; + private PyExpression myInitializer; + private List myOccurrences = Collections.emptyList(); + private Collection mySuggestedNames; + + public IntroduceOperation(Project project, + Editor editor, + PsiFile file, + String name, + boolean replaceAll, + boolean hasConstructor, + boolean testClass) { + myProject = project; + myEditor = editor; + myFile = file; + myName = name; + myReplaceAll = replaceAll; + myHasConstructor = hasConstructor; + myTestClass = testClass; + } + + public String getName() { + return myName; + } + + public void setName(@Nullable String name) { + myName = name; + } + + public Project getProject() { + return myProject; + } + + public Editor getEditor() { + return myEditor; + } + + public PsiFile getFile() { + return myFile; + } + + public PsiElement getElement() { + return myElement; + } + + public void setElement(PsiElement element) { + myElement = element; + } + + public IntroduceHandler.InitPlace getInitPlace() { + return myInitPlace; + } + + public void setInitPlace(IntroduceHandler.InitPlace initPlace) { + myInitPlace = initPlace; + } + + public boolean isReplaceAll() { + return myReplaceAll; + } + + public void setReplaceAll(boolean replaceAll) { + myReplaceAll = replaceAll; + } + + public PyExpression getInitializer() { + return myInitializer; + } + + public void setInitializer(PyExpression initializer) { + myInitializer = initializer; + } + + public List getOccurrences() { + return myOccurrences; + } + + public void setOccurrences(List occurrences) { + myOccurrences = occurrences; + } + + public Collection getSuggestedNames() { + return mySuggestedNames; + } + + public void setSuggestedNames(Collection suggestedNames) { + mySuggestedNames = suggestedNames; + } + + public boolean hasConstructor() { + return myHasConstructor; + } + + public boolean isTestClass() { + return myTestClass; + } +} diff --git a/python/src/com/jetbrains/python/refactoring/introduce/PyIntroduceDialog.java b/python/src/com/jetbrains/python/refactoring/introduce/PyIntroduceDialog.java index f9276fd47f6c..77875e714474 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/PyIntroduceDialog.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/PyIntroduceDialog.java @@ -42,26 +42,22 @@ public class PyIntroduceDialog extends DialogWrapper implements PyIntroduceSetti private final String myHelpId; public PyIntroduceDialog(@NotNull final Project project, - @NotNull PyExpression expression, @NotNull final String caption, @NotNull final IntroduceValidator validator, - final int occurrencesCount, - final Collection possibleNames, final String helpId, - boolean hasConstructor, - boolean isTestClass) { + final IntroduceOperation operation) { super(project, true); - myOccurrencesCount = occurrencesCount; + myOccurrencesCount = operation.getOccurrences().size(); myValidator = validator; myProject = project; - myExpression = expression; + myExpression = operation.getInitializer(); myHelpId = helpId; - setUpNameComboBox(possibleNames); + setUpNameComboBox(operation.getSuggestedNames()); setModal(true); setTitle(caption); init(); - setupDialog(hasConstructor, isTestClass); + setupDialog(operation.hasConstructor(), operation.isTestClass()); updateControls(); } diff --git a/python/src/com/jetbrains/python/refactoring/introduce/constant/ConstantIntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java similarity index 79% rename from python/src/com/jetbrains/python/refactoring/introduce/constant/ConstantIntroduceHandler.java rename to python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java index 1b1e0d99debf..8a3a4d04a677 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/constant/ConstantIntroduceHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java @@ -7,25 +7,23 @@ import com.jetbrains.python.codeInsight.imports.AddImportHelper; import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.refactoring.introduce.IntroduceHandler; +import com.jetbrains.python.refactoring.introduce.IntroduceOperation; import org.jetbrains.annotations.NotNull; import java.util.Collection; -import java.util.List; /** * @author Alexey.Ivanov */ -public class ConstantIntroduceHandler extends IntroduceHandler { - public ConstantIntroduceHandler() { +public class PyIntroduceConstantHandler extends IntroduceHandler { + public PyIntroduceConstantHandler() { super(new ConstantValidator(), PyBundle.message("refactoring.introduce.constant.dialog.title")); } @Override protected PsiElement addDeclaration(@NotNull final PsiElement expression, @NotNull final PsiElement declaration, - @NotNull final List occurrences, - final boolean replaceAll, - InitPlace initInConstructor) { + @NotNull final IntroduceOperation operation) { final PsiElement anchor = expression.getContainingFile(); assert anchor instanceof PyFile; return anchor.addBefore(declaration, AddImportHelper.getFileInsertPosition((PyFile)anchor)); diff --git a/python/src/com/jetbrains/python/refactoring/introduce/field/FieldIntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java similarity index 62% rename from python/src/com/jetbrains/python/refactoring/introduce/field/FieldIntroduceHandler.java rename to python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java index 652f2bdfd1d5..75c694706baf 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/field/FieldIntroduceHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java @@ -1,6 +1,9 @@ package com.jetbrains.python.refactoring.introduce.field; +import com.intellij.lang.ASTNode; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.CaretModel; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; @@ -13,6 +16,7 @@ import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.introduce.inplace.InplaceVariableIntroducer; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.util.Function; import com.jetbrains.python.PyNames; @@ -20,27 +24,26 @@ import com.jetbrains.python.actions.AddFieldQuickFix; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyFunctionBuilder; import com.jetbrains.python.refactoring.introduce.IntroduceHandler; +import com.jetbrains.python.refactoring.introduce.IntroduceOperation; import com.jetbrains.python.refactoring.introduce.variable.PyIntroduceVariableHandler; import com.jetbrains.python.testing.PythonUnitTestUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; +import javax.swing.*; +import java.util.*; /** * @author Dennis.Ushakov */ -public class FieldIntroduceHandler extends IntroduceHandler { +public class PyIntroduceFieldHandler extends IntroduceHandler { - public FieldIntroduceHandler() { + public PyIntroduceFieldHandler() { super(new IntroduceFieldValidator(), RefactoringBundle.message("introduce.field.title")); } public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { - performAction(project, editor, file, null, InitPlace.SAME_METHOD, false, true, isTestClass(file, editor)); + performAction(new IntroduceOperation(project, editor, file, null, false, true, isTestClass(file, editor))); } private static boolean isTestClass(PsiFile file, Editor editor) { @@ -76,22 +79,23 @@ public class FieldIntroduceHandler extends IntroduceHandler { @Nullable @Override - protected PsiElement addDeclaration(@NotNull PsiElement expression, @NotNull PsiElement declaration, @NotNull List occurrences, - boolean replaceAll, InitPlace initInConstructor) { + protected PsiElement addDeclaration(@NotNull PsiElement expression, @NotNull PsiElement declaration, @NotNull IntroduceOperation operation) { final PsiElement expr = expression instanceof PyClass ? expression : expression.getParent(); PsiElement anchor = PyUtil.getContainingClassOrSelf(expr); assert anchor instanceof PyClass; final PyClass clazz = (PyClass)anchor; final Project project = anchor.getProject(); - if (initInConstructor == InitPlace.CONSTRUCTOR && !inConstructor(clazz, expression)) { + if (operation.getInitPlace() == InitPlace.CONSTRUCTOR && !inConstructor(expression)) { return AddFieldQuickFix.addFieldToInit(project, clazz, "", new AddFieldDeclaration(declaration)); - } else if (initInConstructor == InitPlace.SET_UP) { - return addFieldToSetUp(project, clazz, declaration); + } else if (operation.getInitPlace() == InitPlace.SET_UP) { + return addFieldToSetUp(clazz, new AddFieldDeclaration(declaration)); } - return PyIntroduceVariableHandler.doIntroduceVariable(expression, declaration, occurrences, replaceAll); + return PyIntroduceVariableHandler.doIntroduceVariable(expression, declaration, operation.getOccurrences(), operation.isReplaceAll()); } - private boolean inConstructor(@Nullable PyClass clazz, @NotNull PsiElement expression) { + private static boolean inConstructor(@NotNull PsiElement expression) { + final PsiElement expr = expression instanceof PyClass ? expression : expression.getParent(); + PyClass clazz = PyUtil.getContainingClassOrSelf(expr); PsiElement current = PyUtil.getConcealingParent(expression); if (clazz != null && current != null && current instanceof PyFunction) { PyFunction init = clazz.findMethodByName(PyNames.INIT, false); @@ -103,18 +107,18 @@ public class FieldIntroduceHandler extends IntroduceHandler { } @Nullable - private static PsiElement addFieldToSetUp(Project project, PyClass clazz, PsiElement declaration) { + private static PsiElement addFieldToSetUp(PyClass clazz, final Function callback) { final PyFunction init = clazz.findMethodByName(PythonUnitTestUtil.TESTCASE_SETUP_NAME, false); if (init != null) { - return AddFieldQuickFix.appendToInit(init, new AddFieldDeclaration(declaration)); + return AddFieldQuickFix.appendToMethod(init, callback); } final PyFunctionBuilder builder = new PyFunctionBuilder(PythonUnitTestUtil.TESTCASE_SETUP_NAME); builder.parameter(PyNames.CANONICAL_SELF); - PyFunction setUp = builder.buildFunction(project, LanguageLevel.getDefault()); + PyFunction setUp = builder.buildFunction(clazz.getProject(), LanguageLevel.getDefault()); final PyStatementList statements = clazz.getStatementList(); final PsiElement anchor = statements.getFirstChild(); setUp = (PyFunction)statements.addBefore(setUp, anchor); - return AddFieldQuickFix.appendToInit(setUp, new AddFieldDeclaration(declaration)); + return AddFieldQuickFix.appendToMethod(setUp, callback); } @Override @@ -217,4 +221,89 @@ public class FieldIntroduceHandler extends IntroduceHandler { text.replaceFirst(PyNames.CANONICAL_SELF + "\\.", self_name + ".")); } } + + @Override + protected void performInplaceIntroduce(IntroduceOperation operation) { + final PyAssignmentStatement statement = performRefactoring(operation); + // put caret on identifier after "self." + putCaretOnFieldName(operation.getEditor(), statement); + PyTargetExpression target = (PyTargetExpression) statement.getTargets() [0]; + final List occurrences = operation.getOccurrences(); + final InplaceVariableIntroducer introducer = + new PyInplaceFieldIntroducer(target, operation, occurrences); + introducer.performInplaceRename(false, new LinkedHashSet(operation.getSuggestedNames())); + } + + private static void putCaretOnFieldName(Editor editor, PyAssignmentStatement statement) { + final PsiElement elementAtOffset = statement.getContainingFile().findElementAt(editor.getCaretModel().getOffset()); + PyQualifiedExpression qExpr = PsiTreeUtil.getParentOfType(elementAtOffset, PyQualifiedExpression.class); + if (qExpr != null && qExpr.getQualifier() == null) { + qExpr = PsiTreeUtil.getParentOfType(qExpr, PyQualifiedExpression.class); + } + if (qExpr != null) { + final ASTNode nameElement = qExpr.getNameElement(); + if (nameElement != null) { + final int offset = nameElement.getTextRange().getStartOffset(); + editor.getCaretModel().moveToOffset(offset); + } + } + } + + private static class PyInplaceFieldIntroducer extends InplaceVariableIntroducer { + private final PyTargetExpression myTarget; + private final PyIntroduceFieldPanel myPanel; + + public PyInplaceFieldIntroducer(PyTargetExpression target, + IntroduceOperation operation, + List occurrences) { + super(target, operation.getEditor(), operation.getProject(), "Introduce Field", + occurrences.toArray(new PsiElement[occurrences.size()]), null); + myTarget = target; + if (!inConstructor(target)) { + myPanel = new PyIntroduceFieldPanel(myProject, operation.isTestClass()); + } + else { + myPanel = null; + } + } + + @Override + protected PsiElement checkLocalScope() { + return myTarget.getContainingFile(); + } + + @Override + protected JComponent getComponent() { + return myPanel == null ? null : myPanel.getRootPanel(); + } + + @Override + public void finish() { + super.finish(); + if (myPanel != null && myPanel.getInitPlace() != InitPlace.SAME_METHOD) { + final AccessToken accessToken = ApplicationManager.getApplication().acquireWriteActionLock(getClass()); + try { + final PyAssignmentStatement initializer = PsiTreeUtil.getParentOfType(myTarget, PyAssignmentStatement.class); + assert initializer != null; + final Function callback = new Function() { + @Override + public PyStatement fun(String s) { + return initializer; + } + }; + final PyClass pyClass = PyUtil.getContainingClassOrSelf(initializer); + if (myPanel.getInitPlace() == InitPlace.CONSTRUCTOR) { + AddFieldQuickFix.addFieldToInit(myProject, pyClass, "", callback); + } + else if (myPanel.getInitPlace() == InitPlace.SET_UP) { + addFieldToSetUp(pyClass, callback); + } + initializer.delete(); + } + finally { + accessToken.finish(); + } + } + } + } } diff --git a/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldPanel.form b/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldPanel.form new file mode 100644 index 000000000000..2f0c34f27925 --- /dev/null +++ b/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldPanel.form @@ -0,0 +1,40 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldPanel.java b/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldPanel.java new file mode 100644 index 000000000000..3a0fdb340a72 --- /dev/null +++ b/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldPanel.java @@ -0,0 +1,38 @@ +package com.jetbrains.python.refactoring.introduce.field; + +import com.intellij.openapi.project.Project; +import com.intellij.refactoring.introduce.inplace.KeyboardComboSwitcher; +import com.jetbrains.python.refactoring.introduce.IntroduceHandler; + +import javax.swing.*; + +/** + * @author yole + */ +public class PyIntroduceFieldPanel { + private JPanel myRootPanel; + private JComboBox myInitializerPlaceCombo; + + public PyIntroduceFieldPanel(Project project, boolean testClass) { + KeyboardComboSwitcher.setupActions(myInitializerPlaceCombo, project); + if (testClass) { + ((DefaultComboBoxModel) myInitializerPlaceCombo.getModel()).addElement("setUp() method"); + } + } + + public JPanel getRootPanel() { + return myRootPanel; + } + + public IntroduceHandler.InitPlace getInitPlace() { + final int index = myInitializerPlaceCombo.getSelectedIndex(); + switch (index) { + case 1: + return IntroduceHandler.InitPlace.CONSTRUCTOR; + case 2: + return IntroduceHandler.InitPlace.SET_UP; + default: + return IntroduceHandler.InitPlace.SAME_METHOD; + } + } +} diff --git a/python/src/com/jetbrains/python/refactoring/introduce/variable/PyIntroduceVariableHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/variable/PyIntroduceVariableHandler.java index 7c0146a86b3e..1e3473c0e3f3 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/variable/PyIntroduceVariableHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/variable/PyIntroduceVariableHandler.java @@ -5,6 +5,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.psi.PyStatement; import com.jetbrains.python.refactoring.introduce.IntroduceHandler; +import com.jetbrains.python.refactoring.introduce.IntroduceOperation; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -20,10 +21,8 @@ public class PyIntroduceVariableHandler extends IntroduceHandler { @Override protected PsiElement addDeclaration(@NotNull final PsiElement expression, @NotNull final PsiElement declaration, - @NotNull final List occurrences, - final boolean replaceAll, - InitPlace initInConstructor) { - return doIntroduceVariable(expression, declaration, occurrences, replaceAll); + @NotNull IntroduceOperation operation) { + return doIntroduceVariable(expression, declaration, operation.getOccurrences(), operation.isReplaceAll()); } public static PsiElement doIntroduceVariable(PsiElement expression, diff --git a/python/testData/formatter/lambdaColon.py b/python/testData/formatter/lambdaColon.py new file mode 100644 index 000000000000..b46986036c6b --- /dev/null +++ b/python/testData/formatter/lambdaColon.py @@ -0,0 +1 @@ +lambda o:o.fullName() diff --git a/python/testData/formatter/lambdaColon_after.py b/python/testData/formatter/lambdaColon_after.py new file mode 100644 index 000000000000..497e4dc07ffa --- /dev/null +++ b/python/testData/formatter/lambdaColon_after.py @@ -0,0 +1 @@ +lambda o: o.fullName() diff --git a/python/testData/psi/ConditionalParenLambda.txt b/python/testData/psi/ConditionalParenLambda.txt index 0b46ac94c48c..b520d5732a99 100644 --- a/python/testData/psi/ConditionalParenLambda.txt +++ b/python/testData/psi/ConditionalParenLambda.txt @@ -25,7 +25,7 @@ PyFile:ConditionalParenLambda.py PyParameterList PyNamedParameter('x') PsiElement(Py:IDENTIFIER)('x') - PsiElement(Py:COLON)(':') + PsiElement(Py:COLON)(':') PsiWhiteSpace(' ') PyConditionalExpression PyReferenceExpression: x diff --git a/python/testData/psi/LambdaComprehension.txt b/python/testData/psi/LambdaComprehension.txt index 6131eb274b0c..ebd6e914655e 100644 --- a/python/testData/psi/LambdaComprehension.txt +++ b/python/testData/psi/LambdaComprehension.txt @@ -16,7 +16,8 @@ PyFile:LambdaComprehension.py PyLambdaExpression PsiElement(Py:LAMBDA_KEYWORD)('lambda') PyParameterList - PsiElement(Py:COLON)(':') + + PsiElement(Py:COLON)(':') PsiWhiteSpace(' ') PyReferenceExpression: True PsiElement(Py:IDENTIFIER)('True') @@ -25,7 +26,8 @@ PyFile:LambdaComprehension.py PyLambdaExpression PsiElement(Py:LAMBDA_KEYWORD)('lambda') PyParameterList - PsiElement(Py:COLON)(':') + + PsiElement(Py:COLON)(':') PsiWhiteSpace(' ') PyReferenceExpression: False PsiElement(Py:IDENTIFIER)('False') diff --git a/python/testData/psi/LambdaConditional.txt b/python/testData/psi/LambdaConditional.txt index 472135ee99e1..e5c424b8287c 100644 --- a/python/testData/psi/LambdaConditional.txt +++ b/python/testData/psi/LambdaConditional.txt @@ -6,7 +6,7 @@ PyFile:LambdaConditional.py PyParameterList PyNamedParameter('x') PsiElement(Py:IDENTIFIER)('x') - PsiElement(Py:COLON)(':') + PsiElement(Py:COLON)(':') PsiWhiteSpace(' ') PyConditionalExpression PyNumericLiteralExpression diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index 814518e7d63b..e4e5b7ae80a7 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -135,6 +135,10 @@ public class PyFormatterTest extends PyLightFixtureTestCase { CodeStyleSettingsManager.getInstance().getSettings(myFixture.getProject()).ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true; doTest(); } + + public void testLambdaColon() { + doTest(); + } public void testPsiFormatting() { // IDEA-69724 String initial = diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceConstantTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceConstantTest.java index c0ccdfbdd217..e656d57c9773 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceConstantTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceConstantTest.java @@ -2,7 +2,7 @@ package com.jetbrains.python.refactoring; import com.jetbrains.python.fixtures.PyLightFixtureTestCase; import com.jetbrains.python.psi.LanguageLevel; -import com.jetbrains.python.refactoring.introduce.constant.ConstantIntroduceHandler; +import com.jetbrains.python.refactoring.introduce.constant.PyIntroduceConstantHandler; /** * @author yole @@ -32,7 +32,7 @@ public class PyIntroduceConstantTest extends PyLightFixtureTestCase { private void doTest() { myFixture.configureByFile("/refactoring/introduceConstant/" + getTestName(true) + ".py"); - ConstantIntroduceHandler handler = new ConstantIntroduceHandler(); + PyIntroduceConstantHandler handler = new PyIntroduceConstantHandler(); handler.performAction(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile(), "a", true, false, false); myFixture.checkResultByFile("/refactoring/introduceConstant/" + getTestName(true) + ".after.py"); } diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceFieldTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceFieldTest.java index 35b4cbdbf27e..de405ea2213a 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceFieldTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceFieldTest.java @@ -2,7 +2,8 @@ package com.jetbrains.python.refactoring; import com.jetbrains.python.fixtures.PyLightFixtureTestCase; import com.jetbrains.python.refactoring.introduce.IntroduceHandler; -import com.jetbrains.python.refactoring.introduce.field.FieldIntroduceHandler; +import com.jetbrains.python.refactoring.introduce.IntroduceOperation; +import com.jetbrains.python.refactoring.introduce.field.PyIntroduceFieldHandler; /** * @author yole @@ -22,8 +23,10 @@ public class PyIntroduceFieldTest extends PyLightFixtureTestCase { private void doTest(IntroduceHandler.InitPlace initPlace) { myFixture.configureByFile("/refactoring/introduceField/" + getTestName(true) + ".py"); - FieldIntroduceHandler handler = new FieldIntroduceHandler(); - handler.performAction(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile(), "a", initPlace, true, false, false); + PyIntroduceFieldHandler handler = new PyIntroduceFieldHandler(); + final IntroduceOperation introduceOperation = new IntroduceOperation(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile(), "a", true, false, false); + introduceOperation.setInitPlace(initPlace); + handler.performAction(introduceOperation); myFixture.checkResultByFile("/refactoring/introduceField/" + getTestName(true) + ".after.py"); } }