Merge remote branch 'origin/master'

This commit is contained in:
Ekaterina Tuzova
2011-08-28 17:53:34 +04:00
26 changed files with 610 additions and 133 deletions
+3 -2
View File
@@ -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:
@@ -227,6 +227,7 @@
<elementDescriptionProvider implementation="com.jetbrains.python.findUsages.PyElementDescriptionProvider"/>
<fileStructureGroupRuleProvider implementation="com.jetbrains.python.findUsages.PyFunctionGroupingRuleProvider" id="py-function"/>
<usageTypeProvider implementation="com.jetbrains.python.findUsages.PyUsageTypeProvider"/>
<importFilteringRule implementation="com.jetbrains.python.findUsages.PyImportFilteringRule"/>
<languageInjector implementation="com.jetbrains.python.codeInsight.regexp.PythonRegexpInjector"/>
<lang.parserDefinition language="PythonRegExp"
@@ -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();
@@ -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<PsiElement> 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<PsiElement> allMethods = new ArrayList<PsiElement>();
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());
}
}
@@ -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<PsiElement> myAllElements;
protected PyFunctionFindUsagesHandler(@NotNull PsiElement psiElement) {
super(psiElement);
myAllElements = null;
}
protected PyFunctionFindUsagesHandler(@NotNull PsiElement psiElement, List<PsiElement> 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();
}
}
@@ -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;
}
}
@@ -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);
}
@@ -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() {
@@ -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<PsiReferenc
if (!(element instanceof PyFunction)) {
return;
}
final PyFunction function = (PyFunction)element;
if (!PyNames.INIT.equals(function.getName())) {
return;
final AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock();
String className;
SearchScope searchScope;
PyFunction function;
try {
function = (PyFunction)element;
if (!PyNames.INIT.equals(function.getName())) {
return;
}
final PyClass pyClass = function.getContainingClass();
if (pyClass == null) {
return;
}
className = pyClass.getName();
if (className == null) {
return;
}
searchScope = queryParameters.getEffectiveSearchScope();
if (searchScope instanceof GlobalSearchScope) {
searchScope = GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope)searchScope, PythonFileType.INSTANCE);
}
}
final PyClass pyClass = function.getContainingClass();
if (pyClass == null) {
return;
}
final String className = pyClass.getName();
if (className == null) {
return;
finally {
accessToken.finish();
}
SearchScope searchScope = queryParameters.getEffectiveSearchScope();
if (searchScope instanceof GlobalSearchScope) {
searchScope = GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope)searchScope, PythonFileType.INSTANCE);
}
queryParameters.getOptimizer().searchWord(className, searchScope, UsageSearchContext.IN_CODE, true, function);
}
@@ -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();
}
@@ -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));
@@ -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();
}
}
}
}
}
@@ -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="&amp;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>
@@ -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;
}
}
}
@@ -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,
+1
View File
@@ -0,0 +1 @@
lambda o:o.fullName()
@@ -0,0 +1 @@
lambda o: o.fullName()
@@ -25,7 +25,7 @@ PyFile:ConditionalParenLambda.py
PyParameterList
PyNamedParameter('x')
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:COLON)(':')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyConditionalExpression
PyReferenceExpression: x
+4 -2
View File
@@ -16,7 +16,8 @@ PyFile:LambdaComprehension.py
PyLambdaExpression
PsiElement(Py:LAMBDA_KEYWORD)('lambda')
PyParameterList
PsiElement(Py:COLON)(':')
<empty list>
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)(':')
<empty list>
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyReferenceExpression: False
PsiElement(Py:IDENTIFIER)('False')
+1 -1
View File
@@ -6,7 +6,7 @@ PyFile:LambdaConditional.py
PyParameterList
PyNamedParameter('x')
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:COLON)(':')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyConditionalExpression
PyNumericLiteralExpression
@@ -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 =
@@ -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");
}
}