IJPL-247511 Ability to switch new rd completion per editor

(cherry picked from commit 224eefc45dc3b730edac9fedea2d41e50f9667ad)

GitOrigin-RevId: 7a7bfb344a68d7544a8042d065e1bc73df85d334
This commit is contained in:
Max Medvedev
2026-06-25 20:22:09 +00:00
committed by intellij-monorepo-bot
parent adebb18685
commit 66612629d6
21 changed files with 85 additions and 37 deletions
@@ -85,7 +85,7 @@ public class JavaTypedHandlerBase extends TypedHandlerDelegate {
}
protected void autoPopupMemberLookup(@NotNull Project project, @NotNull Editor editor) {
if (NewRdCompletionSupport.isFrontendRdCompletionOn()) {
if (NewRdCompletionSupport.isFrontendRdCompletionOn(editor)) {
AutoPopupController.getInstance(project).scheduleAutoPopup(editor, new FrontendAutoPopupMemberLookupCondition(editor));
}
}
@@ -552,7 +552,7 @@ public class JavaTypedHandlerBase extends TypedHandlerDelegate {
@Override
public @NotNull Result checkAutoPopup(char charTyped, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
if (NewRdCompletionSupport.isFrontendRdCompletionOn()) {
if (NewRdCompletionSupport.isFrontendRdCompletionOn(editor)) {
return doCheckAutoPopup(charTyped, project, editor, file);
}
else {
@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl;
import com.intellij.codeInsight.completion.CompletionContributor;
@@ -122,7 +122,7 @@ public class DaemonRespondToChangesPerfTest extends ProductionDaemonAnalyzerTest
@NonNls String filePath = "/psi/resolve/Thinlet.java";
configureByFile(filePath);
type(' ');
CompletionContributor.forLanguage(getFile().getLanguage());
CompletionContributor.forLanguage(getFile().getLanguage(), getEditor());
myTestDaemonCodeAnalyzer.waitHighlighting(getFile(), HighlightSeverity.ERROR);
int N = Math.max(5, Timings.adjustAccordingToMySpeed(80, false));
@@ -219,7 +219,7 @@ public class DaemonRespondToChangesPerfTest extends ProductionDaemonAnalyzerTest
configureByFile(filePath);
type(' ');
CompletionContributor.forLanguage(getFile().getLanguage());
CompletionContributor.forLanguage(getFile().getLanguage(), getEditor());
long s = System.currentTimeMillis();
myTestDaemonCodeAnalyzer.waitHighlighting(getFile(), HighlightSeverity.ERROR);
if (DEBUG) {
@@ -30,5 +30,6 @@
<extensionPoint qualifiedName="com.intellij.weigher" beanClass="com.intellij.psi.WeigherExtensionPoint" dynamic="true">
<with attribute="implementationClass" implements="com.intellij.psi.Weigher"/>
</extensionPoint>
<extensionPoint qualifiedName="com.intellij.newRdCompletionVeto" interface="com.intellij.codeInsight.completion.NewRdCompletionVeto" dynamic="true"/>
</extensionPoints>
</idea-plugin>
@@ -236,13 +236,22 @@ public abstract class CompletionContributor implements PossiblyDumbAware {
return ReadAction.computeBlocking(() -> {
PsiElement position = parameters.getPosition();
Language language = PsiUtilCore.getLanguageAtOffset(position.getContainingFile(), parameters.getOffset());
return forLanguageHonorDumbness(language, position.getProject());
return forLanguageHonorDumbness(language, position.getProject(), parameters.getEditor());
});
}
/**
* @deprecated Use {@link forLanguage(Language, Editor)} direcly
*/
@ApiStatus.Internal
@Deprecated
public static @NotNull List<CompletionContributor> forLanguage(@NotNull Language language) {
boolean isRDFrontend = NewRdCompletionSupport.isFrontendRdCompletionOn() && PlatformUtils.isJetBrainsClient();
return forLanguage(language, null);
}
@ApiStatus.Internal
public static @NotNull List<CompletionContributor> forLanguage(@NotNull Language language, @Nullable Editor editor) {
boolean isRDFrontend = NewRdCompletionSupport.isFrontendRdCompletionOn(editor) && PlatformUtils.isJetBrainsClient();
List<CompletionContributor> contributors;
if (isRDFrontend) {
@@ -274,8 +283,8 @@ public abstract class CompletionContributor implements PossiblyDumbAware {
}
@ApiStatus.Internal
public static @NotNull List<CompletionContributor> forLanguageHonorDumbness(@NotNull Language language, @NotNull Project project) {
List<CompletionContributor> contributors = forLanguage(language);
public static @NotNull List<CompletionContributor> forLanguageHonorDumbness(@NotNull Language language, @NotNull Project project, @NotNull Editor editor) {
List<CompletionContributor> contributors = forLanguage(language, editor);
return DumbService.getInstance(project).filterByDumbAwareness(contributors);
}
@@ -1,4 +1,4 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.completion.group.GroupedCompletionContributor;
@@ -189,7 +189,7 @@ public abstract class CompletionResultSet implements Consumer<LookupElement> {
@NotNull Consumer<? super CompletionResult> consumer,
boolean stop) {
//grouped contributors are not allowed to be used in runRemainingContributors from other contributors
if (GroupedCompletionContributor.isGroupEnabledInApp() &&
if (GroupedCompletionContributor.isGroupEnabled(parameters.getEditor()) &&
contributor instanceof GroupedCompletionContributor groupedCompletionContributor &&
groupedCompletionContributor.groupIsEnabled(parameters)) {
return;
@@ -60,7 +60,7 @@ public abstract class CompletionService {
@NotNull PrefixMatcher matcher,
@NotNull Consumer<? super CompletionResult> consumer) {
List<CompletionContributor> contributors = CompletionContributor.forParameters(parameters);
boolean groupEnabledInApp = GroupedCompletionContributor.isGroupEnabledInApp();
boolean groupEnabledInApp = GroupedCompletionContributor.isGroupEnabled(parameters.getEditor());
int startingIndex = from != null ? contributors.indexOf(from) + 1 : 0;
for (int i = startingIndex; i < contributors.size(); i++) {
@@ -97,7 +97,7 @@ public abstract class CompletionService {
protected void getVariantsFromGroupContributors(@NotNull CompletionParameters parameters,
@NotNull PrefixMatcher matcher,
@NotNull Consumer<? super CompletionResult> consumer) {
if (!GroupedCompletionContributor.isGroupEnabledInApp()) {
if (!GroupedCompletionContributor.isGroupEnabled(parameters.getEditor())) {
return;
}
final List<CompletionContributor> contributors = CompletionContributor.forParameters(parameters);
@@ -5,13 +5,14 @@ import com.intellij.codeInsight.lookup.Lookup
import com.intellij.openapi.components.serviceOrNull
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.registry.Registry
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
interface NewRdCompletionSupport {
fun isFrontendRdCompletionOnImpl(): Boolean
fun isFrontendRdCompletionOnImpl(editor: Editor?): Boolean
/**
* Schedule autopopup for the given editor and completion type.
@@ -45,7 +46,7 @@ interface NewRdCompletionSupport {
* @return true if `remdev.completion.on.frontend` registry flag is enabled AND this is a host or a client of a remoteDev session.
*/
@JvmStatic
fun isFrontendRdCompletionOn(): Boolean = getInstance().isFrontendRdCompletionOnImpl()
fun isFrontendRdCompletionOn(editor: Editor?): Boolean = getInstance().isFrontendRdCompletionOnImpl(editor)
/**
* @return logs an error with a given [message] if `remdev.completion.on.frontend.report.suboptimal.usage` registry flag is enabled.
@@ -68,7 +69,7 @@ interface NewRdCompletionSupport {
}
private object NoOpNewCompletionSupport : NewRdCompletionSupport {
override fun isFrontendRdCompletionOnImpl(): Boolean = false
override fun isFrontendRdCompletionOnImpl(editor: Editor?): Boolean = false
override fun scheduleAutopopupOnFrontend(project: Project, editor: Editor, completionType: CompletionType) = false
@@ -79,4 +80,14 @@ private object NoOpNewCompletionSupport : NewRdCompletionSupport {
override fun noPsiAvailable(editor: Editor) {}
override fun isBackendCompletionActionAvailableImpl(editor: Editor): Boolean = false
}
}
@ApiStatus.Internal
object NewRdCompletionVetoSupport {
private val ep_name: ExtensionPointName<NewRdCompletionVeto> = ExtensionPointName("com.intellij.newRdCompletionVeto")
fun isAllowed(editor: Editor?): Boolean = editor == null || !isVetoed(editor)
private fun isVetoed(editor: Editor): Boolean =
ep_name.findFirstSafe { veto -> veto.veto(editor) } != null
}
@@ -0,0 +1,23 @@
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.completion
import com.intellij.openapi.editor.Editor
import org.jetbrains.annotations.ApiStatus
/**
* Allows forbidding new frontend-based completion support in RemoteDev
*
* ```
* internal class MyLangNewRdVeto : NewRdCompletionVeto {
* override fun veto(editor: Editor): Boolean {
* val project = editor.project ?: return false
* val file = PsiUtilBase.getPsiFileInEditor(editor, project) ?: return false
* return file.language == MyLang.INSTANCE
* }
* }
* ```
*/
@ApiStatus.Internal
interface NewRdCompletionVeto {
fun veto(editor: Editor): Boolean
}
@@ -1,15 +1,16 @@
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.completion.group
import com.intellij.openapi.editor.Editor
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
interface GroupedCompletion {
/**
* Use [GroupedCompletionContributor.isGroupEnabledInApp] instead
* Use [GroupedCompletionContributor.isGroupEnabled] instead
*
* Determines whether the grouped code completion feature is enabled at the application level.
* @return true if grouped code completion is enabled at the application level, otherwise false.
*/
fun isEnabled(): Boolean
fun isEnabled(editor: Editor?): Boolean
}
@@ -3,9 +3,11 @@ package com.intellij.codeInsight.completion.group;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Represents a contributor for grouped code completion. Implementations of this interface are intended to define specific
@@ -24,7 +26,7 @@ public interface GroupedCompletionContributor {
*
* @return true if grouped code completion is enabled at the application level, otherwise false.
*/
static boolean isGroupEnabledInApp() {
return ApplicationManager.getApplication().getService(GroupedCompletion.class).isEnabled();
static boolean isGroupEnabled(@Nullable Editor editor) {
return ApplicationManager.getApplication().getService(GroupedCompletion.class).isEnabled(editor);
}
}
@@ -1,4 +1,4 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.multiverse.CodeInsightContext;
@@ -95,7 +95,7 @@ public final class CompletionInitializationUtil {
DumbModeAccessType.RELIABLE_DATA_ONLY.ignoreDumbMode(() -> {
Project project = psiFile.getProject();
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
List<CompletionContributor> contributors = CompletionContributor.forLanguageHonorDumbness(context.getPositionLanguage(), project);
List<CompletionContributor> contributors = CompletionContributor.forLanguageHonorDumbness(context.getPositionLanguage(), project, editor);
for (CompletionContributor contributor : contributors) {
current.set(contributor);
contributor.beforeCompletion(context);
@@ -4,6 +4,7 @@ package com.intellij.codeInsight.completion.group
import com.intellij.codeInsight.completion.NewRdCompletionSupport
import com.intellij.idea.AppMode
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.registry.Registry
import com.intellij.util.PlatformUtils
@@ -11,7 +12,7 @@ internal class GroupedCompletionImpl : GroupedCompletion {
/**
* @see com.intellij.codeInsight.completion.command.configuration.AppCommandCompletionSettings.calculateFromRegistry
*/
override fun isEnabled(): Boolean {
override fun isEnabled(editor: Editor?): Boolean {
if (!Registry.`is`("ide.completion.group.enabled", false)) {
return false
}
@@ -31,7 +32,7 @@ internal class GroupedCompletionImpl : GroupedCompletion {
return true
}
if (NewRdCompletionSupport.isFrontendRdCompletionOn() && NewRdCompletionSupport.getInstance().isFrontendForIntelliJBackend()) {
if (NewRdCompletionSupport.isFrontendRdCompletionOn(editor) && NewRdCompletionSupport.getInstance().isFrontendForIntelliJBackend()) {
return true
}
@@ -384,7 +384,7 @@ sealed class CompletionPhase @ApiStatus.Internal constructor(
@JvmStatic
fun loadContributorsOutsideEdt(editor: Editor, file: PsiFile) {
ThreadingAssertions.assertBackgroundThread()
CompletionContributor.forLanguage(PsiUtilCore.getLanguageAtOffset(file, editor.getCaretModel().offset))
CompletionContributor.forLanguage(PsiUtilCore.getLanguageAtOffset(file, editor.getCaretModel().offset), editor)
}
@ApiStatus.Internal
@@ -203,8 +203,8 @@ public final class CompletionProgressIndicator extends ProgressIndicatorBase imp
myAdvertiserChanges.offer(() -> this.lookup.getAdvertiser().clearAdvertisements());
myArranger = GroupedCompletionContributor.isGroupEnabledInApp() ? new GroupCompletionLookupArrangerImpl(this)
: new CompletionLookupArrangerImpl(this);
myArranger = GroupedCompletionContributor.isGroupEnabled(myEditor) ? new GroupCompletionLookupArrangerImpl(this)
: new CompletionLookupArrangerImpl(this);
this.lookup.setArranger(myArranger);
this.lookup.addLookupListener(myLookupListener);
@@ -287,7 +287,7 @@ public final class CompletionProgressIndicator extends ProgressIndicatorBase imp
DumbModeAccessType.RELIABLE_DATA_ONLY.ignoreDumbMode(() -> {
for (CompletionContributor contributor :
CompletionContributor.forLanguageHonorDumbness(initContext.getPositionLanguage(), initContext.getProject())) {
CompletionContributor.forLanguageHonorDumbness(initContext.getPositionLanguage(), initContext.getProject(), initContext.getEditor())) {
ProgressManager.checkCanceled();
contributor.duringCompletion(initContext);
}
@@ -94,7 +94,7 @@ public abstract class BaseCodeCompletionAction extends DumbAwareAction implement
}
if (IdeProductMode.isFrontend()) {
return NewRdCompletionSupport.isFrontendRdCompletionOn();
return NewRdCompletionSupport.isFrontendRdCompletionOn(editor);
}
if (IdeProductMode.isBackend()) {
@@ -265,7 +265,7 @@ internal class CommandCompletionProvider(val contributor: CommandCompletionContr
private fun enableFastShown(parameters: CompletionParameters) {
if (Registry.`is`("ide.completion.command.faster.paint")) {
if (!GroupedCompletionContributor.isGroupEnabledInApp()) return
if (!GroupedCompletionContributor.isGroupEnabled(parameters.editor)) return
if (!contributor.groupIsEnabled(parameters)) return
val completionProgressIndicator = parameters.process as? CompletionProgressIndicator
val count = completionProgressIndicator?.lookup?.list?.model?.size ?: 0
@@ -130,7 +130,7 @@ internal class AppCommandCompletionSettings(
// production
if (
PlatformUtils.isIntelliJ() ||
NewRdCompletionSupport.isFrontendRdCompletionOn() && NewRdCompletionSupport.getInstance().isFrontendForIntelliJBackend()
NewRdCompletionSupport.isFrontendRdCompletionOn(null) && NewRdCompletionSupport.getInstance().isFrontendForIntelliJBackend()
) {
if (Registry.`is`("ide.completion.command.force.enabled")) {
return true
@@ -41,7 +41,7 @@ class CommandCompletionConfigurableProvider : ConfigurableProvider() {
{ r -> settings.state.setEnabled(r) })
.contextHelp(CodeInsightBundle.message("options.command.completion.display.comment"))
}
if (GroupedCompletionContributor.isGroupEnabledInApp()) {
if (GroupedCompletionContributor.isGroupEnabled(null)) {
indent {
row {
checkBox(CodeInsightBundle.message("options.command.completion.show.group"))
@@ -73,7 +73,7 @@ final class TypedAutoPopupImpl {
}
language = element.getLanguage();
}
List<CompletionContributor> contributors = CompletionContributor.forLanguageHonorDumbness(language, file.getProject());
List<CompletionContributor> contributors = CompletionContributor.forLanguageHonorDumbness(language, file.getProject(), editor);
if (contributors.isEmpty()) {
return false;
}
@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.template.postfix.settings
import com.intellij.codeInsight.CodeInsightBundle
@@ -63,7 +63,7 @@ internal class PostfixTemplatesConfigurableUi : Disposable {
postfixTemplatesGroupCompletion = checkBox(CodeInsightBundle.message("postfix.completion.option.group.enabled"))
.component
}
}.visible(GroupedCompletionContributor.isGroupEnabledInApp())
}.visible(GroupedCompletionContributor.isGroupEnabled(null))
row {
completionEnabledCheckbox = checkBox(CodeInsightBundle.message("postfix.completion.option.autopopup"))
.component
@@ -72,7 +72,7 @@ public final class PostfixTemplatesSettings implements PersistentStateComponent<
}
public boolean isShowAsSeparateGroup() {
return showAsSeparateGroup && GroupedCompletionContributor.isGroupEnabledInApp();
return showAsSeparateGroup && GroupedCompletionContributor.isGroupEnabled(null);
}
public void setShowAsSeparateGroup(boolean showAsSeparateGroup) {