mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
make 'Introduce local variable' intention available at parameter placeholder after completion
This commit is contained in:
+39
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 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.
|
||||
@@ -16,16 +16,21 @@
|
||||
package com.intellij.codeInsight.intention.impl;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.codeInsight.completion.CompletionMemory;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.refactoring.BaseRefactoringIntentionAction;
|
||||
import com.intellij.refactoring.introduceVariable.IntroduceEmptyVariableHandler;
|
||||
import com.intellij.refactoring.introduceVariable.IntroduceVariableHandler;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Danila Ponomarenko
|
||||
*/
|
||||
@@ -48,6 +53,8 @@ public class IntroduceVariableIntentionAction extends BaseRefactoringIntentionAc
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getTypeOfUnfilledParameter(editor, element) != null) return true;
|
||||
|
||||
final PsiExpressionStatement statement = detectExpressionStatement(element);
|
||||
if (statement == null){
|
||||
return false;
|
||||
@@ -61,6 +68,12 @@ public class IntroduceVariableIntentionAction extends BaseRefactoringIntentionAc
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
|
||||
PsiType type = getTypeOfUnfilledParameter(editor, element);
|
||||
if (type != null) {
|
||||
new IntroduceEmptyVariableHandler().invoke(editor, element.getContainingFile(), type);
|
||||
return;
|
||||
}
|
||||
|
||||
final PsiExpressionStatement statement = detectExpressionStatement(element);
|
||||
if (statement == null){
|
||||
return;
|
||||
@@ -85,4 +98,29 @@ public class IntroduceVariableIntentionAction extends BaseRefactoringIntentionAc
|
||||
return prevSibling instanceof PsiExpressionStatement ? (PsiExpressionStatement)prevSibling
|
||||
: PsiTreeUtil.getParentOfType(element, PsiExpressionStatement.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiType getTypeOfUnfilledParameter(@NotNull Editor editor, @NotNull PsiElement element) {
|
||||
if (element.getParent() instanceof PsiExpressionList && element.getParent().getParent() instanceof PsiMethodCallExpression) {
|
||||
PsiJavaToken leftBoundary = PsiTreeUtil.getPrevSiblingOfType(element, PsiJavaToken.class);
|
||||
PsiJavaToken rightBoundary = element instanceof PsiJavaToken ? (PsiJavaToken)element
|
||||
: PsiTreeUtil.getNextSiblingOfType(element, PsiJavaToken.class);
|
||||
if (leftBoundary != null && rightBoundary != null &&
|
||||
CharArrayUtil.isEmptyOrSpaces(editor.getDocument().getImmutableCharSequence(),
|
||||
leftBoundary.getTextRange().getEndOffset(),
|
||||
rightBoundary.getTextRange().getStartOffset())) {
|
||||
PsiMethod method = CompletionMemory.getChosenMethod((PsiCall)element.getParent().getParent());
|
||||
if (method != null) {
|
||||
List<PsiJavaToken> allTokens = PsiTreeUtil.getChildrenOfTypeAsList(element.getParent(), PsiJavaToken.class);
|
||||
PsiParameterList parameterList = method.getParameterList();
|
||||
int parameterIndex = allTokens.indexOf(leftBoundary);
|
||||
if (parameterIndex >= 0 && parameterIndex < parameterList.getParametersCount()) {
|
||||
PsiParameter parameter = parameterList.getParameters()[parameterIndex];
|
||||
return parameter.getType();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.refactoring.introduceVariable;
|
||||
|
||||
import com.intellij.codeInsight.completion.JavaCompletionUtil;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.codeInsight.template.*;
|
||||
import com.intellij.codeInsight.template.impl.ConstantNode;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.SuggestedNameInfo;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.refactoring.HelpID;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class IntroduceEmptyVariableHandler {
|
||||
private static final String VARIABLE_NAME = "IntroducedVariable";
|
||||
private static final String TYPE_NAME = "Type";
|
||||
|
||||
public void invoke(@NotNull Editor editor, @NotNull PsiFile file, @NotNull PsiType type) {
|
||||
Project project = file.getProject();
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
PsiElement at = file.findElementAt(offset);
|
||||
PsiElement anchorStatement = RefactoringUtil.getParentStatement(at, false);
|
||||
if (anchorStatement == null) {
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, RefactoringBundle.getCannotRefactorMessage(
|
||||
RefactoringBundle.message("invalid.expression.context")),
|
||||
RefactoringBundle.message("introduce.variable.title"), HelpID.INTRODUCE_VARIABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
SuggestedNameInfo suggestedNameInfo = getSuggestedVariableNames(project, type, at);
|
||||
assert suggestedNameInfo.names.length > 0;
|
||||
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
Document document = editor.getDocument();
|
||||
String initialVariableName = suggestedNameInfo.names[0];
|
||||
document.insertString(offset, initialVariableName);
|
||||
PsiDocumentManager.getInstance(project).commitDocument(document);
|
||||
|
||||
PsiElement context = anchorStatement.getParent();
|
||||
PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
|
||||
String value = PsiTypesUtil.getDefaultValueOfType(type);
|
||||
PsiExpression initializer = elementFactory.createExpressionFromText(value, anchorStatement);
|
||||
PsiDeclarationStatement declaration = elementFactory
|
||||
.createVariableDeclarationStatement(initialVariableName, type, initializer, context);
|
||||
if (RefactoringUtil.isLoopOrIf(context)) {
|
||||
declaration = (PsiDeclarationStatement)RefactoringUtil.putStatementInLoopBody(declaration, context, anchorStatement);
|
||||
context = declaration.getParent();
|
||||
} else {
|
||||
declaration = (PsiDeclarationStatement)context.addBefore(declaration, anchorStatement);
|
||||
}
|
||||
PsiLocalVariable localVariable = (PsiLocalVariable)declaration.getDeclaredElements()[0];
|
||||
PsiTypeElement typeElement = localVariable.getTypeElement();
|
||||
PsiReference reference = ReferencesSearch.search(localVariable, new LocalSearchScope(context)).findFirst();
|
||||
assert reference != null;
|
||||
PsiElement variableReference = reference.getElement();
|
||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document);
|
||||
|
||||
TemplateBuilderImpl templateBuilder = new TemplateBuilderImpl(context);
|
||||
templateBuilder.replaceElement(typeElement, TYPE_NAME, new ConstantNode(typeElement.getText()), true, true);
|
||||
templateBuilder.replaceElement(localVariable.getNameIdentifier(), VARIABLE_NAME, new MyExpression(suggestedNameInfo), true);
|
||||
templateBuilder.replaceElement(variableReference, VARIABLE_NAME, (String)null, false);
|
||||
templateBuilder.replaceElement(ObjectUtils.assertNotNull(localVariable.getInitializer()), "");
|
||||
templateBuilder.setEndVariableAfter(variableReference);
|
||||
Template template = templateBuilder.buildInlineTemplate();
|
||||
|
||||
editor.getCaretModel().moveToOffset(context.getTextOffset());
|
||||
TemplateManager.getInstance(project).startTemplate(editor, template);
|
||||
});
|
||||
}
|
||||
|
||||
private static SuggestedNameInfo getSuggestedVariableNames(@NotNull Project project, PsiType type, PsiElement at) {
|
||||
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
|
||||
SuggestedNameInfo nameInfo = codeStyleManager.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, type);
|
||||
String[] strings = JavaCompletionUtil.completeVariableNameForRefactoring(codeStyleManager, type, VariableKind.LOCAL_VARIABLE, nameInfo);
|
||||
SuggestedNameInfo.Delegate delegate = new SuggestedNameInfo.Delegate(strings, nameInfo);
|
||||
return codeStyleManager.suggestUniqueVariableName(delegate, at, true);
|
||||
}
|
||||
|
||||
private static class MyExpression extends Expression {
|
||||
private final SuggestedNameInfo myNameInfo;
|
||||
|
||||
private MyExpression(SuggestedNameInfo info) {
|
||||
myNameInfo = info;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Result calculateResult(ExpressionContext context) {
|
||||
return new TextResult(myNameInfo.names[0]);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Result calculateQuickResult(ExpressionContext context) {
|
||||
return calculateResult(context);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public LookupElement[] calculateLookupItems(ExpressionContext context) {
|
||||
LookupElement[] elements = new LookupElement[myNameInfo.names.length];
|
||||
for (int i = 0; i < myNameInfo.names.length; i++) {
|
||||
String name = myNameInfo.names[i];
|
||||
elements[i] = LookupElementBuilder.create(name);
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -21,6 +21,7 @@ import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager;
|
||||
import com.intellij.codeInsight.hint.ParameterInfoController;
|
||||
import com.intellij.codeInsight.hints.JavaInlayParameterHintsProvider;
|
||||
import com.intellij.codeInsight.hints.Option;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation;
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
@@ -249,6 +250,46 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase {
|
||||
myFixture.checkResultWithInlays("class C { void m() { Character.forDigit(<hint text=\"digit:\"/>1,<hint text=\"radix:\"/>2 ) } }");
|
||||
}
|
||||
|
||||
public void testIntroduceVariableIntention() throws Exception {
|
||||
myFixture.configureByText(JavaFileType.INSTANCE, "class C {\n" +
|
||||
" void m() {\n" +
|
||||
" Character.for<caret>\n" +
|
||||
" }\n" +
|
||||
"}");
|
||||
complete("forDigit");
|
||||
waitForAllAsyncStuff();
|
||||
IntentionAction intention = myFixture.findSingleIntention("Introduce local variable");
|
||||
assertNotNull(intention);
|
||||
myFixture.launchAction(intention);
|
||||
myFixture.checkResult("class C {\n" +
|
||||
" void m() {\n" +
|
||||
" int i = ;\n" +
|
||||
" Character.forDigit(i<caret>, )\n" +
|
||||
" }\n" +
|
||||
"}");
|
||||
}
|
||||
|
||||
public void testIntroduceVariableIntentionInIfWithoutBraces() throws Exception {
|
||||
myFixture.configureByText(JavaFileType.INSTANCE, "class C {\n" +
|
||||
" void m() {\n" +
|
||||
" if (true) Character.for<caret>\n" +
|
||||
" }\n" +
|
||||
"}");
|
||||
complete("forDigit");
|
||||
waitForAllAsyncStuff();
|
||||
IntentionAction intention = myFixture.findSingleIntention("Introduce local variable");
|
||||
assertNotNull(intention);
|
||||
myFixture.launchAction(intention);
|
||||
myFixture.checkResult("class C {\n" +
|
||||
" void m() {\n" +
|
||||
" if (true) {\n" +
|
||||
" int i = ;\n" +
|
||||
" Character.forDigit(i<caret>, )\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}");
|
||||
}
|
||||
|
||||
private void waitForParameterInfoUpdate() throws TimeoutException {
|
||||
ParameterInfoController.waitForDelayedActions(getEditor(), 1, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 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.
|
||||
@@ -32,6 +32,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -164,7 +165,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
|
||||
* Adds end variable after the specified element
|
||||
*/
|
||||
public void setEndVariableAfter(PsiElement element) {
|
||||
element = element.getNextSibling();
|
||||
element = PsiTreeUtil.nextLeaf(element);
|
||||
setEndVariableBefore(element);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user