mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote branch 'origin/master'
This commit is contained in:
@@ -45,7 +45,7 @@ public class AddFieldQuickFix implements LocalQuickFix {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiElement appendToInit(PyFunction init, Function<String, PyStatement> callback) {
|
||||
public static PsiElement appendToMethod(PyFunction init, Function<String, PyStatement> 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();
|
||||
|
||||
@@ -11,8 +11,8 @@ import com.jetbrains.python.refactoring.classes.extractSuperclass.PyExtractSuper
|
||||
import com.jetbrains.python.refactoring.classes.pullUp.PyPullUpHandler;
|
||||
import com.jetbrains.python.refactoring.classes.pushDown.PyPushDownHandler;
|
||||
import com.jetbrains.python.refactoring.extractmethod.PyExtractMethodHandler;
|
||||
import com.jetbrains.python.refactoring.introduce.constant.ConstantIntroduceHandler;
|
||||
import com.jetbrains.python.refactoring.introduce.field.FieldIntroduceHandler;
|
||||
import com.jetbrains.python.refactoring.introduce.constant.PyIntroduceConstantHandler;
|
||||
import com.jetbrains.python.refactoring.introduce.field.PyIntroduceFieldHandler;
|
||||
import com.jetbrains.python.refactoring.introduce.variable.PyIntroduceVariableHandler;
|
||||
|
||||
/**
|
||||
@@ -26,12 +26,12 @@ public class RefactoringProvider extends RefactoringSupportProvider {
|
||||
|
||||
@Override
|
||||
public RefactoringActionHandler getIntroduceConstantHandler() {
|
||||
return new ConstantIntroduceHandler();
|
||||
return new PyIntroduceConstantHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefactoringActionHandler getIntroduceFieldHandler() {
|
||||
return new FieldIntroduceHandler();
|
||||
return new PyIntroduceFieldHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,6 +19,8 @@ import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.refactoring.IntroduceTargetChooser;
|
||||
import com.intellij.refactoring.RefactoringActionHandler;
|
||||
import com.intellij.refactoring.introduce.inplace.InplaceVariableIntroducer;
|
||||
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
@@ -57,21 +59,34 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
while (true);
|
||||
}
|
||||
|
||||
protected static void ensureName(IntroduceOperation operation) {
|
||||
if (operation.getName() == null) {
|
||||
final Collection<String> 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<PyExpression>() {
|
||||
@Override
|
||||
public void pass(PyExpression pyExpression) {
|
||||
performActionOnElement(editor, pyExpression, name, initInConstructor, replaceAll, hasConstructor, isTestClass);
|
||||
operation.setElement(pyExpression);
|
||||
performActionOnElement(operation);
|
||||
}
|
||||
}, new Function<PyExpression, String>() {
|
||||
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<PsiElement> 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<PsiElement>(editor)
|
||||
.showChooser(operation.getElement(), operation.getOccurrences(), new Pass<OccurrencesChooser.ReplaceChoice>() {
|
||||
@Override
|
||||
public void pass(OccurrencesChooser.ReplaceChoice replaceChoice) {
|
||||
operation.setReplaceAll(replaceChoice == OccurrencesChooser.ReplaceChoice.ALL);
|
||||
performInplaceIntroduce(operation);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
occurrences = Collections.emptyList();
|
||||
performIntroduceWithDialog(operation);
|
||||
}
|
||||
Collection<String> 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<PsiElement> occurrences = operation.getOccurrences();
|
||||
final InplaceVariableIntroducer<PsiElement> introducer =
|
||||
new PyInplaceVariableIntroducer(target, operation, occurrences);
|
||||
introducer.performInplaceRename(false, new LinkedHashSet<String>(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<PsiElement> 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<PyAssignmentStatement>(project, expression.getContainingFile()) {
|
||||
protected void run(final Result<PyAssignmentStatement> result) throws Throwable {
|
||||
final Pair<PsiElement, TextRange> 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<PsiElement> newOccurrences = new ArrayList<PsiElement>();
|
||||
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<PsiElement, TextRange> 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<PsiElement> occurrences,
|
||||
final boolean replaceAll,
|
||||
final InitPlace initInConstructor);
|
||||
@NotNull IntroduceOperation operation);
|
||||
|
||||
protected void postRefactoring(PsiElement element) {
|
||||
}
|
||||
|
||||
private static class PyInplaceVariableIntroducer extends InplaceVariableIntroducer<PsiElement> {
|
||||
private final PyTargetExpression myTarget;
|
||||
|
||||
public PyInplaceVariableIntroducer(PyTargetExpression target,
|
||||
IntroduceOperation operation,
|
||||
List<PsiElement> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PsiElement> myOccurrences = Collections.emptyList();
|
||||
private Collection<String> 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<PsiElement> getOccurrences() {
|
||||
return myOccurrences;
|
||||
}
|
||||
|
||||
public void setOccurrences(List<PsiElement> occurrences) {
|
||||
myOccurrences = occurrences;
|
||||
}
|
||||
|
||||
public Collection<String> getSuggestedNames() {
|
||||
return mySuggestedNames;
|
||||
}
|
||||
|
||||
public void setSuggestedNames(Collection<String> suggestedNames) {
|
||||
mySuggestedNames = suggestedNames;
|
||||
}
|
||||
|
||||
public boolean hasConstructor() {
|
||||
return myHasConstructor;
|
||||
}
|
||||
|
||||
public boolean isTestClass() {
|
||||
return myTestClass;
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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();
|
||||
}
|
||||
|
||||
|
||||
+4
-6
@@ -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<PsiElement> 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));
|
||||
+107
-18
@@ -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<PsiElement> 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<String, PyStatement> 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<PsiElement> occurrences = operation.getOccurrences();
|
||||
final InplaceVariableIntroducer<PsiElement> introducer =
|
||||
new PyInplaceFieldIntroducer(target, operation, occurrences);
|
||||
introducer.performInplaceRename(false, new LinkedHashSet<String>(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<PsiElement> {
|
||||
private final PyTargetExpression myTarget;
|
||||
private final PyIntroduceFieldPanel myPanel;
|
||||
|
||||
public PyInplaceFieldIntroducer(PyTargetExpression target,
|
||||
IntroduceOperation operation,
|
||||
List<PsiElement> 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<String, PyStatement> callback = new Function<String, PyStatement>() {
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.refactoring.introduce.field.PyIntroduceFieldPanel">
|
||||
<grid id="27dc6" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="500" height="400"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<focusable value="false"/>
|
||||
</properties>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="7f91c" class="com.intellij.ui.components.JBLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="fa68d"/>
|
||||
<text value="&Initialize in"/>
|
||||
</properties>
|
||||
</component>
|
||||
<vspacer id="31684">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<component id="fa68d" class="javax.swing.JComboBox" binding="myInitializerPlaceCombo">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<model>
|
||||
<item value="current method"/>
|
||||
<item value="constructor"/>
|
||||
</model>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
+38
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -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<PsiElement> 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,
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user