diff --git a/java/java-impl/resources/META-INF/JavaPlugin.xml b/java/java-impl/resources/META-INF/JavaPlugin.xml
index 282828791470..30ebda9da881 100644
--- a/java/java-impl/resources/META-INF/JavaPlugin.xml
+++ b/java/java-impl/resources/META-INF/JavaPlugin.xml
@@ -1196,6 +1196,7 @@
+
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java
index 65a07053ce0c..f0a341995580 100644
--- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java
@@ -75,8 +75,6 @@ import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.JavaDeprecationUtils;
import com.siyeh.ig.psiutils.TypeUtils;
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
-import one.util.streamex.EntryStream;
-import one.util.streamex.StreamEx;
import org.jetbrains.annotations.*;
import java.util.*;
@@ -897,7 +895,7 @@ public final class JavaCompletionContributor extends CompletionContributor imple
}
if (parameters.getInvocationCount() > 0) {
- items.addAll(getInnerScopeVariables(parameters, position));
+ items.addAll(getInnerScopeVariables(position));
}
if (ref.getQualifier() instanceof PsiExpression qualifierExpression &&
@@ -945,56 +943,12 @@ public final class JavaCompletionContributor extends CompletionContributor imple
return null;
}
- private static @Unmodifiable Collection getInnerScopeVariables(CompletionParameters parameters, PsiElement position) {
- PsiElement container = BringVariableIntoScopeFix.getContainer(position);
- if (container == null) return Collections.emptyList();
- Map> variableMap =
- EntryStream.ofTree(container, (depth, element) -> depth > 2 ? null : StreamEx.of(element.getChildren()))
- .values()
- .select(PsiCodeBlock.class)
- .flatArray(PsiCodeBlock::getStatements)
- .select(PsiDeclarationStatement.class)
- .flatArray(PsiDeclarationStatement::getDeclaredElements)
- .select(PsiLocalVariable.class)
- .remove(var -> PsiTreeUtil.isAncestor(var, position, true))
- .toMap(PsiLocalVariable::getName, Optional::of, (v1, v2) -> Optional.empty());
- PsiResolveHelper helper = JavaPsiFacade.getInstance(parameters.getOriginalFile().getProject()).getResolveHelper();
- variableMap.values().removeAll(Collections.singleton(Optional.empty()));
- variableMap.keySet().removeIf(name -> helper.resolveReferencedVariable(name, position) != null);
- int offset = position.getTextRange().getStartOffset();
- variableMap.values().removeIf(v -> v.orElseThrow().getTextRange().getStartOffset() > offset);
- if (variableMap.isEmpty()) return Collections.emptyList();
- return ContainerUtil.map(variableMap.values(), optVar -> {
- assert optVar.isPresent();
- PsiLocalVariable variable = optVar.get();
- String place = getPlace(variable);
- return new VariableLookupItem(variable, JavaBundle.message("completion.inner.scope.tail.text", place)).setPriority(-1);
- });
- }
-
- private static @Nls @NotNull String getPlace(PsiLocalVariable variable) {
- String place = JavaBundle.message("completion.inner.scope");
- PsiCodeBlock block = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class);
- PsiElement statement = block == null ? null : block.getParent();
- if (statement instanceof PsiTryStatement) {
- place = ((PsiTryStatement)statement).getFinallyBlock() == block ? JavaKeywords.TRY + "-" + JavaKeywords.FINALLY : JavaKeywords.TRY;
- }
- else if (statement instanceof PsiCatchSection) {
- place = JavaKeywords.CATCH;
- }
- else if (statement instanceof PsiSynchronizedStatement) {
- place = JavaKeywords.SYNCHRONIZED;
- }
- else if (statement instanceof PsiBlockStatement) {
- PsiElement parent = statement.getParent();
- if (parent instanceof PsiWhileStatement) {
- place = JavaKeywords.WHILE;
- }
- else if (parent instanceof PsiIfStatement) {
- place = ((PsiIfStatement)parent).getThenBranch() == statement ? JavaKeywords.IF + "-then" : JavaKeywords.IF + "-" + JavaKeywords.ELSE;
- }
- }
- return place;
+ private static @Unmodifiable Collection getInnerScopeVariables(PsiElement position) {
+ List list = BringVariableIntoScopeFix.findInnerScopeVariables(position);
+ return ContainerUtil.map(list, variable ->
+ new VariableLookupItem(
+ variable, JavaBundle.message("completion.inner.scope.tail.text", BringVariableIntoScopeFix.getVariableDeclarationPlace(variable)))
+ .setPriority(-1));
}
private static @NotNull List completePermitsListReference(@NotNull CompletionParameters parameters,
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/modcompletion/InnerScopeVariableItemProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/modcompletion/InnerScopeVariableItemProvider.java
new file mode 100644
index 000000000000..e1a4a7caf37f
--- /dev/null
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/modcompletion/InnerScopeVariableItemProvider.java
@@ -0,0 +1,25 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.codeInsight.completion.modcompletion;
+
+import com.intellij.codeInsight.daemon.impl.quickfix.BringVariableIntoScopeFix;
+import com.intellij.java.JavaBundle;
+import com.intellij.modcompletion.ModCompletionItem;
+import com.intellij.modcompletion.ModCompletionItemProvider;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiLocalVariable;
+import org.jetbrains.annotations.NotNullByDefault;
+
+import java.util.function.Consumer;
+
+@NotNullByDefault
+final class InnerScopeVariableItemProvider implements ModCompletionItemProvider {
+ @Override
+ public void provideItems(CompletionContext context, Consumer sink) {
+ if (context.invocationCount() < 1) return;
+ PsiElement position = context.getPosition();
+ for (PsiLocalVariable variable : BringVariableIntoScopeFix.findInnerScopeVariables(position)) {
+ sink.accept(new VariableCompletionItem(
+ variable, JavaBundle.message("completion.inner.scope.tail.text", BringVariableIntoScopeFix.getVariableDeclarationPlace(variable))));
+ }
+ }
+}
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/modcompletion/ReferenceItemProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/modcompletion/ReferenceItemProvider.java
index 5c5a4337a835..65bf77be5486 100644
--- a/java/java-impl/src/com/intellij/codeInsight/completion/modcompletion/ReferenceItemProvider.java
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/modcompletion/ReferenceItemProvider.java
@@ -150,10 +150,6 @@ final class ReferenceItemProvider implements ModCompletionItemProvider {
sink.accept(firstArrayElement);
}
});
-
- //if (context.invocationCount() > 0) {
- // items.addAll(getInnerScopeVariables(parameters, position));
- //}
}
private static @Nullable CommonCompletionItem accessFirstElement(ModCompletionItem item) {
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/modcompletion/VariableCompletionItem.java b/java/java-impl/src/com/intellij/codeInsight/completion/modcompletion/VariableCompletionItem.java
index 373ab1406c86..b616d218942b 100644
--- a/java/java-impl/src/com/intellij/codeInsight/completion/modcompletion/VariableCompletionItem.java
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/modcompletion/VariableCompletionItem.java
@@ -142,12 +142,12 @@ final class VariableCompletionItem extends PsiUpdateCompletionItem
if (toDelete != null && toDelete.isValid()) {
document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
}
- PsiDocumentManager.getInstance(project).commitDocument(document);
}
else if (VariableLookupItem.shouldQualify(field, file.findReferenceAt(updater.getCaretOffset() - 1))) {
qualifyFieldReference(actionContext.offset(), updater, field);
}
}
+ PsiDocumentManager.getInstance(project).commitDocument(document);
PsiReferenceExpression ref = PsiTreeUtil.findElementOfClassAtOffset(file, updater.getCaretOffset() - 1, PsiReferenceExpression.class, false);
if (ref != null) {
diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/BringVariableIntoScopeFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/BringVariableIntoScopeFix.java
index d4c2c8023fb4..48f9ddbe486f 100644
--- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/BringVariableIntoScopeFix.java
+++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/BringVariableIntoScopeFix.java
@@ -5,6 +5,8 @@ import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.intention.PriorityAction;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.codeInspection.util.IntentionName;
+import com.intellij.java.JavaBundle;
+import com.intellij.java.syntax.parser.JavaKeywords;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModCommand;
import com.intellij.modcommand.ModCommandAction;
@@ -16,11 +18,14 @@ import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.util.*;
import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.containers.ContainerUtil;
+import one.util.streamex.EntryStream;
+import one.util.streamex.StreamEx;
+import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.util.HashMap;
-import java.util.Objects;
+import java.util.*;
public final class BringVariableIntoScopeFix implements ModCommandAction {
private static final Logger LOG = Logger.getInstance(BringVariableIntoScopeFix.class);
@@ -182,4 +187,60 @@ public final class BringVariableIntoScopeFix implements ModCommandAction {
PsiExpression initializer = factory.createExpressionFromText(init, variable);
variable.setInitializer(initializer);
}
+
+ /**
+ * @param position PSI position to find the inner scope variables
+ * @return list of variables declared in the scopes nested to the position scope
+ */
+ public static @NotNull List findInnerScopeVariables(PsiElement position) {
+ PsiElement container = getContainer(position);
+ Map> variableMap = Map.of();
+ if (container != null) {
+ variableMap = EntryStream.ofTree(container, (depth, element) -> depth > 2 ? null : StreamEx.of(element.getChildren()))
+ .values()
+ .select(PsiCodeBlock.class)
+ .flatArray(PsiCodeBlock::getStatements)
+ .select(PsiDeclarationStatement.class)
+ .flatArray(PsiDeclarationStatement::getDeclaredElements)
+ .select(PsiLocalVariable.class)
+ .remove(var -> PsiTreeUtil.isAncestor(var, position, true))
+ .toMap(PsiLocalVariable::getName, Optional::of, (v1, v2) -> Optional.empty());
+ PsiResolveHelper helper = JavaPsiFacade.getInstance(container.getProject()).getResolveHelper();
+ variableMap.values().removeAll(Collections.singleton(Optional.empty()));
+ variableMap.keySet().removeIf(name -> helper.resolveReferencedVariable(name, position) != null);
+ int offset = position.getTextRange().getStartOffset();
+ variableMap.values().removeIf(v -> v.orElseThrow().getTextRange().getStartOffset() > offset);
+ }
+ List list = ContainerUtil.map(variableMap.values(), Optional::get);
+ return list;
+ }
+
+ /**
+ * @param variable variable to describe its location
+ * @return the human-readable description of variable's declaration place, useful for variable brought into the scope.
+ */
+ public static @Nls @NotNull String getVariableDeclarationPlace(PsiLocalVariable variable) {
+ String place = JavaBundle.message("completion.inner.scope");
+ PsiCodeBlock block = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class);
+ PsiElement statement = block == null ? null : block.getParent();
+ if (statement instanceof PsiTryStatement) {
+ place = ((PsiTryStatement)statement).getFinallyBlock() == block ? JavaKeywords.TRY + "-" + JavaKeywords.FINALLY : JavaKeywords.TRY;
+ }
+ else if (statement instanceof PsiCatchSection) {
+ place = JavaKeywords.CATCH;
+ }
+ else if (statement instanceof PsiSynchronizedStatement) {
+ place = JavaKeywords.SYNCHRONIZED;
+ }
+ else if (statement instanceof PsiBlockStatement) {
+ PsiElement parent = statement.getParent();
+ if (parent instanceof PsiWhileStatement) {
+ place = JavaKeywords.WHILE;
+ }
+ else if (parent instanceof PsiIfStatement ifStatement) {
+ place = ifStatement.getThenBranch() == statement ? JavaKeywords.IF + "-then" : JavaKeywords.IF + "-" + JavaKeywords.ELSE;
+ }
+ }
+ return place;
+ }
}