inplace change signature: no detection, explicit action

This commit is contained in:
Anna.Kozlova
2016-11-10 10:52:14 +01:00
parent 7aca030c70
commit 848c1f215c
27 changed files with 550 additions and 928 deletions
@@ -54,7 +54,6 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.ClassKind;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PropertyMemberType;
import com.intellij.refactoring.changeSignature.ChangeSignatureGestureDetector;
import com.intellij.util.DocumentUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
@@ -703,14 +702,6 @@ public class QuickFixFactoryImpl extends QuickFixFactory {
@NotNull
@Override
public IntentionAction createSafeDeleteFix(@NotNull PsiElement element) {
if (element instanceof PsiMethod) {
PsiMethod method = (PsiMethod)element;
PsiClass containingClass = method.getContainingClass();
if (method.getReturnType() != null || containingClass != null && Comparing.strEqual(containingClass.getName(), method.getName())) {
//ignore methods with deleted return types as they are always marked as unused without any reason
ChangeSignatureGestureDetector.getInstance(method.getProject()).dismissForElement(method);
}
}
return new SafeDeleteFix(element);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,12 +19,13 @@ import com.intellij.lang.findUsages.DescriptiveNameUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.changeSignature.inplace.InplaceChangeSignature;
import com.intellij.refactoring.util.CanonicalTypes;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.IncorrectOperationException;
@@ -49,8 +50,8 @@ class DetectedJavaChangeInfo extends JavaChangeInfoImpl {
CanonicalTypes.Type newType,
@NotNull ParameterInfoImpl[] newParms,
ThrownExceptionInfo[] newExceptions,
String newName, String oldName) {
super(newVisibility, method, newName, newType, newParms, newExceptions, false, new HashSet<>(), new HashSet<>(), oldName);
String newName, String oldName, final boolean delegate) {
super(newVisibility, method, newName, newType, newParms, newExceptions, delegate, new HashSet<>(), new HashSet<>(), oldName);
final PsiParameter[] parameters = method.getParameterList().getParameters();
myModifiers = new String[parameters.length];
for (int i = 0; i < parameters.length; i++) {
@@ -64,28 +65,17 @@ class DetectedJavaChangeInfo extends JavaChangeInfoImpl {
}
@Nullable
static DetectedJavaChangeInfo createFromMethod(PsiMethod method) {
static DetectedJavaChangeInfo createFromMethod(PsiMethod method, final boolean delegate) {
final String newVisibility = VisibilityUtil.getVisibilityModifier(method.getModifierList());
final PsiType returnType = method.getReturnType();
final CanonicalTypes.Type newReturnType;
final ParameterInfoImpl[] parameterInfos;
try {
newReturnType = returnType != null ? CanonicalTypes.createTypeWrapper(returnType) : null;
parameterInfos = ParameterInfoImpl.fromMethod(method);
for (ParameterInfoImpl parameterInfo : parameterInfos) {
if (!parameterInfo.getTypeWrapper().isValid()) {
return null;
}
}
if (PsiTreeUtil.findChildOfType(method.getParameterList(), PsiErrorElement.class) != null) {
final CanonicalTypes.Type newReturnType = returnType != null ? CanonicalTypes.createTypeWrapper(returnType) : null;
final ParameterInfoImpl[] parameterInfos = ParameterInfoImpl.fromMethod(method);
for (ParameterInfoImpl parameterInfo : parameterInfos) {
if (!parameterInfo.getTypeWrapper().isValid()) {
return null;
}
}
catch (IncorrectOperationException e) {
return null;
}
final DetectedJavaChangeInfo fromMethod = new DetectedJavaChangeInfo(newVisibility, method, newReturnType, parameterInfos, null, method.getName(), method.getName());
final DetectedJavaChangeInfo fromMethod = new DetectedJavaChangeInfo(newVisibility, method, newReturnType, parameterInfos, null, method.getName(), method.getName(), delegate);
final PsiMethod deepestSuperMethod = method.findDeepestSuperMethod();
if (deepestSuperMethod != null) {
if (!deepestSuperMethod.getManager().isInProject(deepestSuperMethod)) return null;
@@ -120,8 +110,8 @@ class DetectedJavaChangeInfo extends JavaChangeInfoImpl {
}
@Nullable
ChangeInfo createNextInfo(final PsiMethod method) {
final DetectedJavaChangeInfo fromMethod = createFromMethod(method);
DetectedJavaChangeInfo createNextInfo(final PsiMethod method, boolean delegate) {
final DetectedJavaChangeInfo fromMethod = createFromMethod(method, delegate);
if (fromMethod == null) return null;
if (!this.equals(fromMethod)) {
if (!createParametersInfo(fromMethod.newParms)) return null;
@@ -144,7 +134,7 @@ class DetectedJavaChangeInfo extends JavaChangeInfoImpl {
try {
final DetectedJavaChangeInfo javaChangeInfo =
new DetectedJavaChangeInfo(newVisibility, method, fromMethod.newReturnType, fromMethod.newParms, getNewExceptions(), method.getName(), getOldName()) {
new DetectedJavaChangeInfo(fromMethod.getNewVisibility(), getMethod(), fromMethod.newReturnType, fromMethod.newParms, getNewExceptions(), method.getName(), getOldName(), delegate) {
@Override
protected void fillOldParams(PsiMethod method) {
oldParameterNames = DetectedJavaChangeInfo.this.getOldParameterNames();
@@ -184,7 +174,7 @@ class DetectedJavaChangeInfo extends JavaChangeInfoImpl {
getNewReturnType(),
(ParameterInfoImpl[])getNewParameters(),
getNewExceptions(), getNewName(),
method.getName()) {
method.getName(), false) {
@Override
protected void fillOldParams(PsiMethod method) {
super.fillOldParams(method);
@@ -263,14 +253,18 @@ class DetectedJavaChangeInfo extends JavaChangeInfoImpl {
boolean perform(ChangeInfo initialChangeInfo, final String oldText, boolean silently) {
final PsiMethod method = getSuperMethod();
Project project = initialChangeInfo.getMethod().getProject();
final PsiMethod currentMethod = (PsiMethod)initialChangeInfo.getMethod();
final TextRange signatureRange = JavaChangeSignatureDetector.getSignatureRange(currentMethod);
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
final Document document = documentManager.getDocument(currentMethod.getContainingFile());
if (silently || ApplicationManager.getApplication().isUnitTestMode()) {
final TextRange signatureRange = JavaChangeSignatureDetector.getSignatureRange(currentMethod);
final String currentSignature = currentMethod.getContainingFile().getText().substring(signatureRange.getStartOffset(),
signatureRange.getEndOffset());
temporallyRevertChanges(currentMethod, oldText);
InplaceChangeSignature.temporallyRevertChanges(JavaChangeSignatureDetector.getSignatureRange(currentMethod), document, oldText, project);
createChangeSignatureProcessor(method).run();
temporallyRevertChanges(currentMethod, currentSignature, JavaChangeSignatureDetector.getSignatureRange(currentMethod));
InplaceChangeSignature
.temporallyRevertChanges(JavaChangeSignatureDetector.getSignatureRange(currentMethod), document, currentSignature, project);
return true;
}
final JavaMethodDescriptor descriptor = new JavaMethodDescriptor(currentMethod) {
@@ -288,7 +282,7 @@ class DetectedJavaChangeInfo extends JavaChangeInfoImpl {
@Override
protected void invokeRefactoring(final BaseRefactoringProcessor processor) {
CommandProcessor.getInstance().executeCommand(myProject, () -> {
temporallyRevertChanges(method, oldText);
InplaceChangeSignature.temporallyRevertChanges(JavaChangeSignatureDetector.getSignatureRange(currentMethod), document, oldText, project);
doRefactor(processor);
}, RefactoringBundle.message("changing.signature.of.0", DescriptiveNameUtil.getDescriptiveName(currentMethod)), null);
}
@@ -299,22 +293,4 @@ class DetectedJavaChangeInfo extends JavaChangeInfoImpl {
};
return dialog.showAndGet();
}
private static void temporallyRevertChanges(final PsiElement psiElement, final String oldText) {
temporallyRevertChanges(psiElement, oldText, psiElement.getTextRange());
}
private static void temporallyRevertChanges(final PsiElement psiElement,
final String oldText,
final TextRange textRange) {
ApplicationManager.getApplication().runWriteAction(() -> {
final PsiFile file = psiElement.getContainingFile();
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(psiElement.getProject());
final Document document = documentManager.getDocument(file);
if (document != null) {
document.replaceString(textRange.getStartOffset(), textRange.getEndOffset(), oldText);
documentManager.commitDocument(document);
}
});
}
}
@@ -15,75 +15,41 @@
*/
package com.intellij.refactoring.changeSignature;
import com.intellij.lang.Language;
import com.intellij.lang.StdLanguages;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.changeSignature.inplace.LanguageChangeSignatureDetector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: anna
* Date: Sep 6, 2010
*/
public class JavaChangeSignatureDetector implements LanguageChangeSignatureDetector {
public class JavaChangeSignatureDetector implements LanguageChangeSignatureDetector<DetectedJavaChangeInfo> {
private static final Logger LOG = Logger.getInstance("#" + JavaChangeSignatureDetector.class.getName());
@NotNull
@Override
public ChangeInfo createInitialChangeInfo(final @NotNull PsiElement element) {
final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class, false);
if (method != null && isInsideMethodSignature(element, method)) {
//do not initialize change signature on return type change
if (element.getTextRange().getEndOffset() <= method.getTextOffset()) return null;
return DetectedJavaChangeInfo.createFromMethod(method);
} else {
final PsiVariable variable = PsiTreeUtil.getParentOfType(element, PsiVariable.class);
if (variable != null) {
return new RenameChangeInfo(variable, null) {
@Override
public Language getLanguage() {
return StdLanguages.JAVA;
}
};
}
}
return null;
public DetectedJavaChangeInfo createInitialChangeInfo(final @NotNull PsiElement element) {
return DetectedJavaChangeInfo.createFromMethod(PsiTreeUtil.getParentOfType(element, PsiMethod.class), false);
}
@Override
public boolean performChange(final ChangeInfo changeInfo, ChangeInfo initialChangeInfo, @NotNull final String oldText, boolean silently) {
if (changeInfo instanceof DetectedJavaChangeInfo) {
return ((DetectedJavaChangeInfo)changeInfo).perform(initialChangeInfo, oldText, silently);
} else if (changeInfo instanceof RenameChangeInfo) {
((RenameChangeInfo)changeInfo).perform();
return true;
}
return false;
public void performChange(final DetectedJavaChangeInfo changeInfo, @NotNull final String oldText) {
changeInfo.perform(changeInfo, oldText, true);
}
@Override
public boolean isChangeSignatureAvailableOnElement(PsiElement element, ChangeInfo currentInfo) {
if (currentInfo instanceof RenameChangeInfo) {
final PsiElement nameIdentifier = ((RenameChangeInfo)currentInfo).getNameIdentifier();
if (nameIdentifier != null) {
final TextRange nameIdentifierTextRange = nameIdentifier.getTextRange();
return nameIdentifierTextRange.contains(element.getTextRange()) ||
nameIdentifierTextRange.getEndOffset() == element.getTextOffset();
}
public boolean isChangeSignatureAvailableOnElement(PsiElement element, DetectedJavaChangeInfo currentInfo) {
final PsiMethod method = currentInfo.getMethod();
TextRange range = method.getTextRange();
PsiCodeBlock body = method.getBody();
if (body != null) {
range = new TextRange(range.getStartOffset(), body.getTextOffset());
}
else if (currentInfo instanceof JavaChangeInfo) {
final PsiMethod method = (PsiMethod)currentInfo.getMethod();
return getSignatureRange(method).contains(element.getTextRange());
}
return false;
return element.getContainingFile() == method.getContainingFile() && range.contains(element.getTextRange());
}
@Override
@@ -92,56 +58,28 @@ public class JavaChangeSignatureDetector implements LanguageChangeSignatureDetec
return PsiTreeUtil.getParentOfType(element, PsiImportList.class) != null;
}
@Nullable
@Override
public TextRange getHighlightingRange(ChangeInfo changeInfo) {
if (changeInfo == null) return null;
if (changeInfo instanceof RenameChangeInfo) {
PsiElement nameIdentifier = ((RenameChangeInfo)changeInfo).getNameIdentifier();
return nameIdentifier != null ? nameIdentifier.getTextRange() : null;
}
public TextRange getHighlightingRange(@NotNull DetectedJavaChangeInfo changeInfo) {
PsiElement method = changeInfo.getMethod();
return method instanceof PsiMethod ? getSignatureRange((PsiMethod)method) : null;
}
@Nullable
@Override
public String extractSignature(PsiElement element, @NotNull ChangeInfo initialChangeInfo) {
final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class, false);
if (method != null && isInsideMethodSignature(element, method) && method == initialChangeInfo.getMethod()) {
final TextRange signatureRange = getSignatureRange(method);
return signatureRange.shiftRight(-signatureRange.getStartOffset()).substring(method.getText());
} else if (element instanceof PsiIdentifier && element.getParent() instanceof PsiNamedElement) {
return element.getText();
}
return null;
return method != null ? getSignatureRange((PsiMethod)method) : null;
}
@Override
public ChangeInfo createNextChangeInfo(String signature, @NotNull final ChangeInfo currentInfo, String initialName) {
public DetectedJavaChangeInfo createNextChangeInfo(String signature, @NotNull final DetectedJavaChangeInfo currentInfo, boolean delegate) {
final PsiElement currentInfoMethod = currentInfo.getMethod();
if (currentInfoMethod == null) {
return null;
}
final Project project = currentInfoMethod.getProject();
if (currentInfo instanceof RenameChangeInfo) {
return currentInfo;
}
final PsiMethod oldMethod = (PsiMethod)currentInfo.getMethod();
final PsiMethod oldMethod = currentInfo.getMethod();
String visibility = "";
PsiClass containingClass = oldMethod.getContainingClass();
if (containingClass != null && containingClass.isInterface()) {
visibility = PsiModifier.PUBLIC + " ";
}
PsiMethod method = JavaPsiFacade.getElementFactory(project).createMethodFromText((visibility + signature).trim(), oldMethod);
return ((DetectedJavaChangeInfo)currentInfo).createNextInfo(method);
}
private static boolean isInsideMethodSignature(PsiElement element, @NotNull PsiMethod method) {
final TextRange textRange = element.getTextRange();
return getSignatureRange(method).contains(textRange);
return currentInfo.createNextInfo(method, delegate);
}
public static TextRange getSignatureRange(PsiMethod method) {
@@ -149,50 +87,4 @@ public class JavaChangeSignatureDetector implements LanguageChangeSignatureDetec
int startOffset = method.getTextRange().getStartOffset();
return new TextRange(startOffset, endOffset);
}
@Override
public boolean isMoveParameterAvailable(PsiElement element, boolean left) {
if (element instanceof PsiParameter) {
final PsiParameter parameter = (PsiParameter)element;
final PsiElement declarationScope = parameter.getDeclarationScope();
if (declarationScope instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)declarationScope;
final int parameterIndex = method.getParameterList().getParameterIndex(parameter);
if (left) {
return parameterIndex > 0;
} else {
return parameterIndex < method.getParameterList().getParametersCount() - 1;
}
}
}
return false;
}
@Override
public void moveParameter(final PsiElement element, final Editor editor, final boolean left) {
final PsiParameter parameter = (PsiParameter)element;
final PsiMethod method = (PsiMethod)parameter.getDeclarationScope();
final int parameterIndex = method.getParameterList().getParameterIndex(parameter);
new WriteCommandAction(element.getProject(), MOVE_PARAMETER){
@Override
protected void run(@NotNull Result result) throws Throwable {
final PsiParameterList parameterList = method.getParameterList();
final PsiParameter[] parameters = parameterList.getParameters();
final int deltaOffset = editor.getCaretModel().getOffset() - parameter.getTextRange().getStartOffset();
final PsiParameter frst = left ? parameters[parameterIndex - 1] : parameter;
final PsiParameter scnd = left ? parameter : parameters[parameterIndex + 1];
final int startOffset = frst.getTextRange().getStartOffset();
final int endOffset = scnd.getTextRange().getEndOffset();
final PsiFile file = method.getContainingFile();
final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(file);
if (document != null) {
final String comma_whitespace_between =
document.getText().substring(frst.getTextRange().getEndOffset(), scnd.getTextRange().getStartOffset());
document.replaceString(startOffset, endOffset, scnd.getText() + comma_whitespace_between + frst.getText());
editor.getCaretModel().moveToOffset(document.getText().indexOf(parameter.getText(), startOffset) + deltaOffset);
}
}
}.execute();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,22 +19,31 @@ import com.intellij.codeInsight.JavaTargetElementEvaluator;
import com.intellij.ide.util.SuperMethodWarningUtil;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.changeClassSignature.ChangeClassSignatureDialog;
import com.intellij.refactoring.changeSignature.inplace.InplaceChangeSignature;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
public class JavaChangeSignatureHandler implements ChangeSignatureHandler {
private static final Logger LOG = Logger.getInstance(JavaChangeSignatureHandler.class);
public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) {
editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
PsiElement element = findTargetMember(file, editor);
@@ -45,16 +54,7 @@ public class JavaChangeSignatureHandler implements ChangeSignatureHandler {
}
private static void invokeOnElement(Project project, @Nullable Editor editor, PsiElement element) {
if (element instanceof PsiMethod) {
/*final ChangeSignatureGestureDetector detector = ChangeSignatureGestureDetector.getInstance(project);
final PsiIdentifier nameIdentifier = ((PsiMethod)element).getNameIdentifier();
if (nameIdentifier != null &&
editor != null &&
editor.getDocument().isWritable() &&
detector.isChangeSignatureAvailable(element)) {
detector.changeSignature(element.getContainingFile(), false);
return;
}*/
if (element instanceof PsiMethod && ((PsiMethod)element).getNameIdentifier() != null) {
invoke((PsiMethod) element, project, editor);
}
else if (element instanceof PsiClass) {
@@ -92,8 +92,54 @@ public class JavaChangeSignatureHandler implements ChangeSignatureHandler {
final PsiClass containingClass = method.getContainingClass();
final PsiReferenceExpression refExpr = editor != null ? JavaTargetElementEvaluator.findReferenceExpression(editor) : null;
final boolean allowDelegation = containingClass != null && (!containingClass.isInterface() || PsiUtil.isLanguageLevel8OrHigher(containingClass));
final DialogWrapper dialog = new JavaChangeSignatureDialog(project, method, allowDelegation, refExpr == null ? method : refExpr);
dialog.show();
InplaceChangeSignature inplaceChangeSignature = InplaceChangeSignature.getCurrentRefactoring(editor);
ChangeInfo initialChange = inplaceChangeSignature != null ? inplaceChangeSignature.getStableChange() : null;
boolean isInplace = Registry.is("inplace.change.signature") && editor != null && editor.getSettings().isVariableInplaceRenameEnabled() && (initialChange == null || initialChange.getMethod() != method);
PsiIdentifier nameIdentifier = method.getNameIdentifier();
LOG.assertTrue(nameIdentifier != null);
if (isInplace) {
CommandProcessor.getInstance().executeCommand(project, () -> new InplaceChangeSignature(project, editor, nameIdentifier), REFACTORING_NAME, null);
}
else {
JavaMethodDescriptor methodDescriptor = new JavaMethodDescriptor(method);
if (initialChange != null) {
JavaChangeInfo currentInfo = (JavaChangeInfo)inplaceChangeSignature.getCurrentInfo();
if (currentInfo != null) {
methodDescriptor = new JavaMethodDescriptor(method) {
@Override
public String getName() {
return currentInfo.getNewName();
}
@Override
public List<ParameterInfoImpl> getParameters() {
return Arrays.asList((ParameterInfoImpl[])currentInfo.getNewParameters());
}
@Override
public String getVisibility() {
return currentInfo.getNewVisibility();
}
@Override
public int getParametersCount() {
return currentInfo.getNewParameters().length;
}
@Nullable
@Override
public String getReturnTypeText() {
return currentInfo.getNewReturnType().getTypeText();
}
};
}
inplaceChangeSignature.cancel();
}
final DialogWrapper dialog = new JavaChangeSignatureDialog(project, methodDescriptor, allowDelegation, refExpr == null ? method : refExpr);
dialog.show();
}
}
private static void invoke(final PsiClass aClass, Editor editor) {