IDEA-229842 Convert compact constructor to canonical

GitOrigin-RevId: 6edd200e51ec300bf1068d2f040b837fea3f6e1f
This commit is contained in:
Tagir Valeev
2020-01-21 17:41:52 +07:00
committed by intellij-monorepo-bot
parent d0f72f2f02
commit b936699772
11 changed files with 188 additions and 0 deletions

View File

@@ -1959,6 +1959,10 @@
<className>com.intellij.codeInsight.intention.impl.lists.JavaJoinArgumentsAction</className>
<category>Java/Other</category>
</intentionAction>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.ConvertCompactConstructorToCanonicalAction</className>
<category>Java/Declaration</category>
</intentionAction>
<externalAnnotationsArtifactsResolver implementation="com.intellij.jarRepository.ExternalAnnotationsRepositoryResolver"/>
<errorQuickFixProvider implementation="com.intellij.codeInsight.daemon.impl.analysis.JavaErrorQuickFixProvider"/>
<fileTypeDetector implementation="com.intellij.openapi.fileTypes.impl.JavaFileTypeDetector"/>

View File

@@ -0,0 +1,75 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.intention.impl;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.generation.RecordConstructorMember;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.JavaPsiRecordUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class ConvertCompactConstructorToCanonicalAction extends PsiElementBaseIntentionAction {
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
PsiMethod method = getMethod(element);
if (method == null) return;
PsiClass recordClass = method.getContainingClass();
if (recordClass == null) return;
PsiMethod prototype = new RecordConstructorMember(recordClass, false).generateRecordConstructor();
PsiModifierList modifierList = method.getModifierList();
prototype.getModifierList().replace(modifierList);
PsiElement beforeModifier = modifierList.getPrevSibling();
if (beforeModifier != null) {
prototype.addRangeBefore(method.getFirstChild(), beforeModifier, prototype.getModifierList());
}
PsiCodeBlock oldBody = Objects.requireNonNull(method.getBody());
PsiCodeBlock body = (PsiCodeBlock)Objects.requireNonNull(prototype.getBody()).replace(oldBody);
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
for (PsiRecordComponent component : recordClass.getRecordComponents()) {
PsiField field = JavaPsiRecordUtil.getFieldForComponent(component);
if (field != null && !HighlightControlFlowUtil.variableDefinitelyAssignedIn(field, body)) {
body.add(factory.createStatementFromText("this." + field.getName() + "=" + field.getName() + ";", body));
}
}
int offset = editor.getCaretModel().getOffset();
if (oldBody.getTextRange().contains(offset)) {
offset += body.getTextRangeInParent().getStartOffset() - oldBody.getTextRangeInParent().getStartOffset();
}
method.replace(prototype);
editor.getCaretModel().moveToOffset(offset);
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@Override
public @NotNull String getText() {
return getFamilyName();
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
PsiMethod method = getMethod(element);
return method != null &&
JavaPsiRecordUtil.isCompactConstructor(method) &&
method.getBody() != null &&
method.getContainingClass() != null &&
method.getContainingClass().isRecord();
}
private static PsiMethod getMethod(@NotNull PsiElement element) {
return PsiTreeUtil.getParentOfType(element, PsiMethod.class, true, PsiLambdaExpression.class, PsiMember.class);
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@Override
public @NotNull String getFamilyName() {
return CodeInsightBundle.message("intention.convert.compact.constructor.to.canonical");
}
}

View File

@@ -0,0 +1,9 @@
record Range(int from, int to) {
<spot>public Range(int from, int to) {
if (from > to) {
from = to;
}
this.from = from;
this.to = to;
}</spot>
}

View File

@@ -0,0 +1,7 @@
record Range(int from, int to) {
<spot>public Range {
if (from > to) {
from = to;
}
}</spot>
}

View File

@@ -0,0 +1,6 @@
<html>
<body>
Converts Java record compact constructor to explicit canonical form.
<!-- tooltip end -->
</body>
</html>

View File

@@ -0,0 +1,22 @@
// "Convert compact constructor to canonical" "true"
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
public record Test(int x, @ParamAnno int y, @FieldAnno int... other) {
@ConstructorAnno
public Test(int x, @ParamAnno int y, int... other) {
this.x = Math.abs(x);
if (other == null) other = new int[0];
this.y = y;
this.other = other;
}
}
@Target(ElementType.PARAMETER)
@interface ParamAnno {}
@Target(ElementType.FIELD)
@interface FieldAnno {}
@Target(ElementType.CONSTRUCTOR)
@interface ConstructorAnno {}

View File

@@ -0,0 +1,10 @@
// "Convert compact constructor to canonical" "true"
record R(int x,int y) {
/*
hello
*/
public R(int x, int y) {<caret>
this.x = x;
this.y = y;
}
}

View File

@@ -0,0 +1,20 @@
// "Convert compact constructor to canonical" "true"
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
public record Test(int x, @ParamAnno int y, @FieldAnno int... other) {
@ConstructorAnno
<caret>public Test {
this.x = Math.abs(x);
if (other == null) other = new int[0];
}
}
@Target(ElementType.PARAMETER)
@interface ParamAnno {}
@Target(ElementType.FIELD)
@interface FieldAnno {}
@Target(ElementType.CONSTRUCTOR)
@interface ConstructorAnno {}

View File

@@ -0,0 +1,8 @@
// "Convert compact constructor to canonical" "true"
record R(int x,int y) {
/*
hello
*/
public R {<caret>
}
}

View File

@@ -0,0 +1,25 @@
/*
* 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.java.codeInsight.intention;
import com.intellij.codeInsight.daemon.LightIntentionActionTestCase;
public class ConvertCompactConstructorToCanonicalActionTest extends LightIntentionActionTestCase {
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/compactToCanonical";
}
}

View File

@@ -270,6 +270,8 @@ intention.wrap.with.unmodifiable.list=Wrap with unmodifiable list
intention.wrap.with.unmodifiable.set=Wrap with unmodifiable set
intention.wrap.with.unmodifiable.map=Wrap with unmodifiable map
intention.convert.compact.constructor.to.canonical=Convert compact constructor to canonical
intention.preview.adv.show.text=Press {0} to open preview
intention.preview.adv.hide.text=Press {0} to hide preview
intention.preview.no.available.text=Preview isn't available