[Java] LSP-425 Refactoring IntroduceVariableBase

- Create better structure for IntroduceVariableBase#getTempContainer
- Use better return type in IntroduceVariableUtil#getIntroduceVariableCandidates

GitOrigin-RevId: 3627b2928d974c977a6d89d18ad0f37792b3ba1c
This commit is contained in:
Georgii Ustinov
2026-01-29 10:44:13 +00:00
committed by intellij-monorepo-bot
parent ae9defa6a9
commit 888f4cc38f
3 changed files with 166 additions and 146 deletions
@@ -53,10 +53,11 @@ public class IntroduceFunctionalVariableHandler extends IntroduceVariableHandler
}
PsiElement anchorStatement =
elements[0] instanceof PsiComment ? elements[0] : CommonJavaRefactoringUtil.getParentStatement(elements[0], false);
TempContainerResult result = getTempContainer(anchorStatement);
ErrorOrContainer errorOrContainer = getTempContainer(anchorStatement);
if (result.errorMessage != null) {
showErrorMessage(project, editor, result.errorMessage);
if (errorOrContainer instanceof ErrorOrContainer.Error(@NlsContexts.DialogMessage String message)) {
showErrorMessage(project, editor, message);
}
PsiElement[] elementsInCopy = IntroduceParameterHandler.getElementsInCopy(project, file, elements);
@@ -40,6 +40,7 @@ import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer;
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser.ReplaceChoice;
import com.intellij.refactoring.introduceField.ElementToWorkOn;
import com.intellij.refactoring.introduceVariable.IntroduceVariableBase.ErrorOrContainer.Container;
import com.intellij.refactoring.listeners.RefactoringEventData;
import com.intellij.refactoring.listeners.RefactoringEventListener;
import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
@@ -64,6 +65,9 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import static com.intellij.refactoring.IntroduceVariableUtil.*;
import static com.intellij.refactoring.introduceVariable.IntroduceVariableBase.IntroduceVariableResult.Context;
public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
public static class JavaReplaceChoice implements OccurrencesChooser.BaseReplaceChoice {
public static final JavaReplaceChoice NO = new JavaReplaceChoice(ReplaceChoice.NO, null, false);
@@ -137,14 +141,15 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
final SelectionModel selectionModel = editor.getSelectionModel();
if (!selectionModel.hasSelection()) {
final int offset = editor.getCaretModel().getOffset();
Pair<TextRange, List<PsiExpression>> rangeAndExpressions = getExpressionsAndSelectionRange(project, editor, file, offset);
TextRange suggestedSelection = rangeAndExpressions.getFirst();
IntroduceVariableCandidates
info = getIntroduceVariableCandidates(project, editor, file, offset);
TextRange suggestedSelection = info.bestRangeToExtractFrom();
if (suggestedSelection != null) {
selectionModel.setSelection(suggestedSelection.getStartOffset(), suggestedSelection.getEndOffset());
}
else {
final PsiElement[] statementsInRange = IntroduceVariableUtil.findStatementsAtOffset(editor, file, offset);
List<PsiExpression> expressions = rangeAndExpressions.getSecond();
final PsiElement[] statementsInRange = findStatementsAtOffset(editor, file, offset);
List<PsiExpression> expressions = info.expressions();
IntroduceTargetChooser.showChooser(editor, expressions,
new Pass<>() {
@Override
@@ -155,7 +160,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
},
new PsiExpressionTrimRenderer.RenderFunction(),
RefactoringBundle.message("introduce.target.chooser.expressions.title"),
IntroduceVariableUtil.preferredSelection(statementsInRange, expressions), ScopeHighlighter.NATURAL_RANGER);
preferredSelection(statementsInRange, expressions), ScopeHighlighter.NATURAL_RANGER);
return;
}
}
@@ -165,18 +170,25 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
}
}
/**
* @deprecated use {@link IntroduceVariableUtil#getIntroduceVariableCandidates(Project, Editor, PsiFile, int)} instead.
*/
@Deprecated(forRemoval = true)
public static @NotNull Pair<@Nullable TextRange, @NotNull List<PsiExpression>> getExpressionsAndSelectionRange(final @NotNull Project project,
final Editor editor,
final @NotNull Editor editor,
final @NotNull PsiFile file,
int offset) {
return IntroduceVariableUtil.getExpressionAndSelectionRange(project, editor, file, offset);
IntroduceVariableCandidates
info = getIntroduceVariableCandidates(project, editor, file, offset);
return new Pair<>(info.bestRangeToExtractFrom(), info.expressions());
}
private boolean invoke(final Project project, final Editor editor, PsiFile file, int startOffset, int endOffset) {
FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.REFACTORING_INTRODUCE_VARIABLE);
PsiDocumentManager.getInstance(project).commitAllDocuments();
return invokeImpl(project, IntroduceVariableUtil.findExpressionInRange(project, file, startOffset, endOffset), editor);
return invokeImpl(project, findExpressionInRange(project, file, startOffset, endOffset), editor);
}
public @NotNull Pair<List<PsiElement>, List<PsiExpression>> getPossibleAnchorsAndOccurrences(final Project project, final PsiExpression expr) {
@@ -203,13 +215,19 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
private @NotNull OccurrencesInfo buildOccurrencesInfo(Project project, PsiExpression expr) {
final PsiElement anchorStatement = getAnchor(expr);
TempContainerResult result = getTempContainer(anchorStatement);
ErrorOrContainer result = getTempContainer(anchorStatement);
if (result.errorMessage != null) {
showErrorMessage(project, null, result.errorMessage);
}
final PsiElement tempContainer = switch (result) {
case Container container -> {
yield container.element();
}
case ErrorOrContainer.Error error -> {
showErrorMessage(project, null, error.message());
yield null;
}
};
final ExpressionOccurrenceManager occurrenceManager = createOccurrenceManager(expr, result.container);
final ExpressionOccurrenceManager occurrenceManager = createOccurrenceManager(expr, tempContainer);
final PsiExpression[] occurrences = occurrenceManager.getOccurrences();
return new OccurrencesInfo(occurrences);
@@ -232,13 +250,13 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
@Nullable PsiElement targetContainer,
@Nullable JavaReplaceChoice replaceChoice,
final Editor editor) {
Result result = getIntroduceVariableContext(project, expr, editor);
IntroduceVariableResult introduceVariableResult = getIntroduceVariableContext(project, expr, editor);
switch (result) {
switch (introduceVariableResult) {
case Context context -> {
return doRefactoring(project, targetContainer, replaceChoice, editor, context);
}
case Error error -> {
case IntroduceVariableResult.Error error -> {
if (error.message != null) {
showErrorMessage(project, editor, error.message);
}
@@ -303,18 +321,18 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
/**
* @return the context necessary for performing "Introduce Variable" refactoring.
*/
public static @NotNull Result getIntroduceVariableContext(@NotNull Project project, @Nullable PsiExpression expr, @Nullable Editor editor) {
public static @NotNull IntroduceVariableBase.IntroduceVariableResult getIntroduceVariableContext(@NotNull Project project, @Nullable PsiExpression expr, @Nullable Editor editor) {
if (expr != null) {
String message = IntroduceVariableUtil.getErrorMessage(expr);
String message = getErrorMessage(expr);
if (message != null) {
return new Error(message);
return new IntroduceVariableResult.Error(message);
}
PsiExpression topLevelExpression = ExpressionUtils.getTopLevelExpression(expr);
if (topLevelExpression.getParent() instanceof PsiField f) {
PsiClass containingClass = f.getContainingClass();
if (containingClass != null && containingClass.isInterface()) {
message = JavaRefactoringBundle.message("introduce.variable.message.cannot.extract.variable.in.interface");
return new Error(message);
return new IntroduceVariableResult.Error(message);
}
}
}
@@ -322,21 +340,21 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
if (expr != null && expr.getParent() instanceof PsiExpressionStatement) {
FeatureUsageTracker.getInstance().triggerFeatureUsed("refactoring.introduceVariable.incompleteStatement");
}
if (IntroduceVariableUtil.LOG.isDebugEnabled()) {
IntroduceVariableUtil.LOG.debug("expression:" + expr);
if (LOG.isDebugEnabled()) {
LOG.debug("expression:" + expr);
}
if (expr == null || !expr.isPhysical()) {
if (ReassignVariableUtil.reassign(editor)) return new Error(null);
if (ReassignVariableUtil.reassign(editor)) return new IntroduceVariableResult.Error(null);
if (expr == null) {
String message = JavaRefactoringBundle.message("selected.block.should.represent.an.expression");
return new Error(message);
return new IntroduceVariableResult.Error(message);
}
}
String enumInSwitchError = RefactoringUtil.checkEnumConstantInSwitchLabel(expr);
if (enumInSwitchError != null) {
return new Error(enumInSwitchError);
return new IntroduceVariableResult.Error(enumInSwitchError);
}
@@ -345,12 +363,12 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
dumbService.computeWithAlternativeResolveEnabled(() -> CommonJavaRefactoringUtil.getTypeByExpressionWithExpectedType(expr));
if (originalType == null || LambdaUtil.notInferredType(originalType)) {
String message = JavaRefactoringBundle.message("unknown.expression.type");
return new Error(message);
return new IntroduceVariableResult.Error(message);
}
if (PsiTypes.voidType().equals(originalType)) {
String message = JavaRefactoringBundle.message("selected.expression.has.void.type");
return new Error(message);
return new IntroduceVariableResult.Error(message);
}
try {
@@ -360,51 +378,53 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
}
catch (IncorrectOperationException ignore) {
String message = JavaRefactoringBundle.message("unknown.expression.type");
return new Error(message);
return new IntroduceVariableResult.Error(message);
}
for (PsiPatternVariable variable : JavaPsiPatternUtil.getExposedPatternVariables(expr)) {
if (ContainerUtil.exists(VariableAccessUtils.getVariableReferences(variable),
ref -> !PsiTreeUtil.isAncestor(expr, ref, true))) {
String message = JavaRefactoringBundle.message("selected.expression.introduces.pattern.variable", variable.getName());
return new Error(message);
return new IntroduceVariableResult.Error(message);
}
}
final PsiElement anchorStatement = getAnchor(expr);
TempContainerResult result = getTempContainer(anchorStatement);
if (result.errorMessage != null) {
return new Error(result.errorMessage, false);
}
ErrorOrContainer errorOrContainer = getTempContainer(anchorStatement);
final PsiElement tempContainer = Objects.requireNonNull(result.container);
return switch (errorOrContainer) {
case Container container -> {
final PsiFile file = Objects.requireNonNull(anchorStatement).getContainingFile();
LOG.assertTrue(file != null, "expr.getContainingFile() == null");
final PsiElement nameSuggestionContext = editor == null ? null : file.findElementAt(editor.getCaretModel().getOffset());
final RefactoringSupportProvider supportProvider = LanguageRefactoringSupport.getInstance().forContext(expr);
final boolean isInplaceAvailableOnDataContext =
supportProvider != null &&
editor != null &&
editor.getSettings().isVariableInplaceRenameEnabled() &&
supportProvider.isInplaceIntroduceAvailable(expr, nameSuggestionContext) &&
!isInJspHolderMethod(expr);
final PsiFile file = Objects.requireNonNull(anchorStatement).getContainingFile();
IntroduceVariableUtil.LOG.assertTrue(file != null, "expr.getContainingFile() == null");
final PsiElement nameSuggestionContext = editor == null ? null : file.findElementAt(editor.getCaretModel().getOffset());
final RefactoringSupportProvider supportProvider = LanguageRefactoringSupport.getInstance().forContext(expr);
final boolean isInplaceAvailableOnDataContext =
supportProvider != null &&
editor != null &&
editor.getSettings().isVariableInplaceRenameEnabled() &&
supportProvider.isInplaceIntroduceAvailable(expr, nameSuggestionContext) &&
!isInJspHolderMethod(expr);
if (isInplaceAvailableOnDataContext) {
final MultiMap<PsiElement, String> conflicts = new MultiMap<>();
checkInLoopCondition(expr, conflicts);
if (!conflicts.isEmpty()) {
yield new IntroduceVariableResult.Error(StringUtil.join(new TreeSet<>(conflicts.values()), "<br>"), false);
}
}
if (isInplaceAvailableOnDataContext) {
final MultiMap<PsiElement, String> conflicts = new MultiMap<>();
checkInLoopCondition(expr, conflicts);
if (!conflicts.isEmpty()) {
return new Error(StringUtil.join(new TreeSet<>(conflicts.values()), "<br>"), false);
final ExpressionOccurrenceManager occurrenceManager = createOccurrenceManager(expr, container.element());
final PsiExpression[] occurrences = occurrenceManager.getOccurrences();
OccurrencesInfo occurrencesInfo = new OccurrencesInfo(occurrences);
yield new Context(expr, originalType, anchorStatement, occurrenceManager, occurrencesInfo, isInplaceAvailableOnDataContext);
}
}
final ExpressionOccurrenceManager occurrenceManager = createOccurrenceManager(expr, tempContainer);
final PsiExpression[] occurrences = occurrenceManager.getOccurrences();
OccurrencesInfo occurrencesInfo = new OccurrencesInfo(occurrences);
return new Context(expr, originalType, anchorStatement, occurrenceManager, occurrencesInfo, isInplaceAvailableOnDataContext);
case ErrorOrContainer.Error error -> {
yield new IntroduceVariableResult.Error(error.message(), false);
}
};
}
private static @Nullable PsiType getNormalizedType(PsiExpression expr) {
@@ -458,38 +478,24 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
return physicalElement != null ? physicalElement : place;
}
/**
* Contract of the class: when {@code errorMessage} is not null, {@code container} must be null and vice versa.
*/
protected static class TempContainerResult {
final @Nullable @NlsContexts.DialogMessage String errorMessage;
final @Nullable PsiElement container;
TempContainerResult(@Nullable @NlsContexts.DialogMessage String errorMessage, @Nullable PsiElement container) {
this.errorMessage = errorMessage;
this.container = container;
}
}
protected static @NotNull TempContainerResult getTempContainer(@Nullable PsiElement anchorStatement) {
protected static @NotNull ErrorOrContainer getTempContainer(@Nullable PsiElement anchorStatement) {
if (anchorStatement == null) {
return new TempContainerResult(
JavaRefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", getRefactoringName()), null
return new ErrorOrContainer.Error(
JavaRefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", getRefactoringName())
);
}
String anchorMessage = getAnchorBeforeMessage(anchorStatement);
if (anchorMessage != null) return new TempContainerResult(anchorMessage, null);
if (anchorMessage != null) return new ErrorOrContainer.Error(anchorMessage);
final PsiElement tempContainer = anchorStatement.getParent();
if (!(tempContainer instanceof PsiCodeBlock) && !CommonJavaRefactoringUtil.isLoopOrIf(tempContainer) && !(tempContainer instanceof PsiLambdaExpression) && (tempContainer.getParent() instanceof PsiLambdaExpression)) {
return new TempContainerResult(
JavaRefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", getRefactoringName()), null
return new ErrorOrContainer.Error(
JavaRefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", getRefactoringName())
);
}
return new TempContainerResult(null, tempContainer);
return new Container(tempContainer);
}
private static ExpressionOccurrenceManager createOccurrenceManager(PsiExpression expr, @Nullable PsiElement tempContainer) {
@@ -845,7 +851,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
.map(place -> (PsiExpression)getPhysicalElement(place))
.groupingBy(e -> PsiTreeUtil.findCommonParent(e, physical),
() -> new TreeMap<>(treeOrder), Collectors.toList());
IntroduceVariableUtil.LOG.assertTrue(!groupByBlock.isEmpty());
LOG.assertTrue(!groupByBlock.isEmpty());
List<PsiExpression> currentOccurrences = new ArrayList<>();
Map<String, Integer> counts = new HashMap<>();
groupByBlock.forEach((parent, occurrences) -> {
@@ -972,7 +978,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
.map(Function.identity())
.pairMap((prev, next) -> text.substring(prev.getKey(), next.getKey()) + next.getValue())
.joining();
IntroduceVariableUtil.LOG.error("Unable to find anchor for a new variable; selectedOccurrences.length = " + selectedOccurrences.length,
LOG.error("Unable to find anchor for a new variable; selectedOccurrences.length = " + selectedOccurrences.length,
new Attachment("source.java", textWithOccurrences));
return;
}
@@ -1030,9 +1036,9 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
chosenAnchor instanceof PsiSwitchLabelStatementBase;
Consumer<? super PsiElement> callback = container -> {
PsiElement anchor = container instanceof PsiLambdaExpression ? getAnchor(container) : container;
TempContainerResult result = getTempContainer(anchor);
if (result.errorMessage != null) {
showErrorMessage(project, editor, result.errorMessage);
ErrorOrContainer errorOrContainer = getTempContainer(anchor);
if (errorOrContainer instanceof ErrorOrContainer.Error(String message)) {
showErrorMessage(project, editor, message);
return;
}
myInplaceIntroducer = new JavaVariableInplaceIntroducer(project, settings, anchor, editor, expr,
@@ -1052,56 +1058,61 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
}
}
/**
* Represents the result of getting the necessary environment for introducing a variable.
* @see IntroduceVariablePass#getIntroduceVariableContext(Project, PsiFile, Editor, TextRange)
* @see IntroduceVariablePass#getIntroduceVariableContext(Project, PsiExpression, Editor)
*/
public sealed interface Result permits Error, Context {}
public sealed interface IntroduceVariableResult permits IntroduceVariableResult.Error, Context {
/**
* Represents a message that will be displayed in UI if there is an error during collecting the context for introduced variable.
* @see IntroduceVariablePass#showErrorMessage(Project, Editor, String)
*/
public static final class Error implements Result {
public final @NlsContexts.DialogMessage @Nullable String message;
/**
* Represents a message that will be displayed in UI if there is an error during collecting the context for introduced variable.
* @see IntroduceVariablePass#showErrorMessage(Project, Editor, String)
*/
final class Error implements IntroduceVariableResult {
public final @NlsContexts.DialogMessage @Nullable String message;
Error(@NlsContexts.DialogMessage @Nullable String message) {
this(message, true);
Error(@NlsContexts.DialogMessage @Nullable String message) {
this(message, true);
}
Error(@NlsContexts.DialogMessage @Nullable String message, boolean shouldWrap) {
if (message == null) {
this.message = null;
}
else if (shouldWrap) {
this.message = RefactoringBundle.getCannotRefactorMessage(message);
}
else {
this.message = message;
}
}
}
Error(@NlsContexts.DialogMessage @Nullable String message, boolean shouldWrap) {
if (message == null) {
this.message = null;
}
else if (shouldWrap) {
this.message = RefactoringBundle.getCannotRefactorMessage(message);
}
else {
this.message = message;
/**
* Represents all the data necessary to introduce the variable.
* @param expression - element that should be extracted into the separate variable.
* @param originalType - type of the expression that should be extracted.
* @param anchorStatement - statement near which the declared variable will be created.
* @param occurrenceManager - stores all occurrences of the expression that should be extracted.
* @param occurrencesInfo - stores additional information about occurrences like whether they are valid for extraction.
* @param isInplaceAvailableOnDataContext - indicates whether inplace refactoring is available for the current context.
*/
record Context(
@NotNull PsiExpression expression,
@NotNull PsiType originalType,
@NotNull PsiElement anchorStatement,
@NotNull ExpressionOccurrenceManager occurrenceManager,
@NotNull OccurrencesInfo occurrencesInfo,
boolean isInplaceAvailableOnDataContext
) implements IntroduceVariableResult {
PsiFile file() {
return anchorStatement.getContainingFile();
}
}
}
/**
* Represents all the data necessary to introduce the variable.
* @param expression - element that should be extracted into the separate variable.
* @param originalType - type of the expression that should be extracted.
* @param anchorStatement - statement near which the declared variable will be created.
* @param occurrenceManager - stores all occurrences of the expression that should be extracted.
* @param occurrencesInfo - stores additional information about occurrences like whether they are valid for extraction.
* @param isInplaceAvailableOnDataContext - indicates whether inplace refactoring is available for the current context.
*/
public record Context(
@NotNull PsiExpression expression,
@NotNull PsiType originalType,
@NotNull PsiElement anchorStatement,
@NotNull ExpressionOccurrenceManager occurrenceManager,
@NotNull OccurrencesInfo occurrencesInfo,
boolean isInplaceAvailableOnDataContext
) implements Result {
PsiFile file() {
return anchorStatement.getContainingFile();
}
protected sealed interface ErrorOrContainer permits Container, ErrorOrContainer.Error {
record Container(@NotNull PsiElement element) implements ErrorOrContainer {}
record Error(@NotNull @NlsContexts.DialogMessage String message) implements ErrorOrContainer {}
}
}
@@ -22,7 +22,6 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
@@ -90,22 +89,23 @@ public final class IntroduceVariableUtil {
}
/**
* @see IntroduceVariableUtil#getExpressionAndSelectionRange(Project, Document, PsiFile, int)
* @see IntroduceVariableUtil#getIntroduceVariableCandidates(Project, Document, PsiFile, int)
*/
public static @NotNull Pair<@Nullable TextRange, @NotNull List<PsiExpression>> getExpressionAndSelectionRange(
public static @NotNull IntroduceVariableCandidates getIntroduceVariableCandidates(
final @NotNull Project project,
final @NotNull Editor editor,
final @NotNull PsiFile file,
int offset
) {
return getExpressionAndSelectionRange(project, editor.getDocument(), file, offset);
return getIntroduceVariableCandidates(project, editor.getDocument(), file, offset);
}
/**
* Searches for the expressions that can be extracted into the variable near the given {@code offset}
* @return {@link TextRange} that includes preferred expression to extract and list of {@link PsiExpression} - candidates that can be extracted as well
* @return the expressions that can be extracted into the variable near the given {@code offset}
* and the recommended {@link TextRange} to select the expression from (could be null).
* @see IntroduceVariableCandidates
*/
public static @NotNull Pair<@Nullable TextRange, @NotNull List<PsiExpression>> getExpressionAndSelectionRange(
public static @NotNull IntroduceVariableCandidates getIntroduceVariableCandidates(
final @NotNull Project project,
final @NotNull Document document,
final @NotNull PsiFile file,
@@ -121,7 +121,7 @@ public final class IntroduceVariableUtil {
final PsiExpression expressionInRange =
findExpressionInRange(project, file, lineRange.getStartOffset(), lineRange.getEndOffset());
if (expressionInRange != null && getErrorMessage(expressionInRange) == null) {
return Pair.create(lineRange, Collections.singletonList(expressionInRange));
return new IntroduceVariableCandidates(lineRange, Collections.singletonList(expressionInRange));
}
}
@@ -130,13 +130,13 @@ public final class IntroduceVariableUtil {
CommonJavaRefactoringUtil.getParentStatement(expression, false) != null ||
PsiTreeUtil.getParentOfType(expression, PsiField.class, true, PsiStatement.class) != null);
if (expressions.isEmpty()) {
return Pair.create(lineRange, Collections.emptyList());
return new IntroduceVariableCandidates(lineRange, Collections.emptyList());
}
else if (!isChooserNeeded(expressions)) {
return Pair.create(expressions.get(0).getTextRange(), expressions);
return new IntroduceVariableCandidates(expressions.getFirst().getTextRange(), expressions);
}
else {
return Pair.create(null, expressions);
return new IntroduceVariableCandidates(null, expressions);
}
}
@@ -144,23 +144,24 @@ public final class IntroduceVariableUtil {
/**
* @return single expression that can be extracted into the variable.
*/
public static PsiExpression findExpressionInRange(Project project, PsiFile file, int startOffset, int endOffset) {
public static PsiExpression findExpressionInRange(@NotNull Project project, @NotNull PsiFile file, int startOffset, int endOffset) {
PsiExpression tempExpr = CodeInsightFrontbackUtil.findExpressionInRange(file, startOffset, endOffset);
if (tempExpr == null) {
PsiElement[] statements = CodeInsightFrontbackUtil.findStatementsInRange(file, startOffset, endOffset);
if (statements.length == 1) {
if (statements[0] instanceof PsiExpressionStatement) {
tempExpr = ((PsiExpressionStatement) statements[0]).getExpression();
PsiElement statement = statements[0];
if (statement instanceof PsiExpressionStatement expressionStatement) {
tempExpr = expressionStatement.getExpression();
}
else if (statements[0] instanceof PsiReturnStatement) {
tempExpr = ((PsiReturnStatement)statements[0]).getReturnValue();
else if (statement instanceof PsiReturnStatement returnStatement) {
tempExpr = returnStatement.getReturnValue();
}
else if (statements[0] instanceof PsiSwitchStatement) {
PsiExpression expr = JavaPsiFacade.getElementFactory(project).createExpressionFromText(statements[0].getText(), statements[0]);
TextRange range = statements[0].getTextRange();
else if (statement instanceof PsiSwitchStatement) {
PsiExpression expr = JavaPsiFacade.getElementFactory(project).createExpressionFromText(statement.getText(), statement);
TextRange range = statement.getTextRange();
final RangeMarker rangeMarker = file.getViewProvider().getDocument().createRangeMarker(range);
expr.putUserData(ElementToWorkOn.TEXT_RANGE, rangeMarker);
expr.putUserData(ElementToWorkOn.PARENT, statements[0]);
expr.putUserData(ElementToWorkOn.PARENT, statement);
return expr;
}
}
@@ -586,4 +587,11 @@ public final class IntroduceVariableUtil {
return parent.replace(createReplacement(ref.getText(), project, prefix, suffix, parent, textRange, new int[1]));
}
}
/**
* Stores information about expressions that can be extracted into the variable near the cursor position.
* @param bestRangeToExtractFrom {@link TextRange} that includes the most appropriate expression to be extracted.
* @param expressions list of expressions that can be extracted into variable.
*/
public record IntroduceVariableCandidates(@Nullable TextRange bestRangeToExtractFrom, @NotNull List<@NotNull PsiExpression> expressions) {}
}