IJPL-207762 introduce frontend-friendly tail types

GitOrigin-RevId: 1dc3f250ae1c85f4453ec4b337b37ff8e8f0cfcc
This commit is contained in:
Max Medvedev
2026-02-16 00:16:08 +00:00
committed by intellij-monorepo-bot
parent 2ae976b0b8
commit 6b08ef1903
15 changed files with 329 additions and 124 deletions
@@ -97,5 +97,7 @@
<codeInsight.completion.command.factory language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.JavaCommandCompletionFactory" />
<completion.frontendFriendlyInsertHandler target="com.intellij.codeInsight.completion.FrontendFriendlyOverridableSpaceInsertHandler"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,45 @@
// 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.TailType
import com.intellij.codeInsight.completion.serialization.InsertHandlerSerializer
import com.intellij.codeInsight.completion.serialization.TailTypeSerializer
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import kotlinx.serialization.Serializable
@Serializable
internal data class FrontendFriendlyOverridableSpaceInsertHandler(
val delegateInsertHandler: FrontendFriendlyInsertHandler?,
val tailType: FrontendFriendlyTailType,
) : FrontendFriendlyInsertHandler {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
require(tailType is TailType) { "Tail type must extend TailType: $tailType" }
val wrapper = LookupElementBuilder.create("").withInsertHandler(delegateInsertHandler)
val lookupElement = OverridableSpace.create(wrapper, tailType)
lookupElement.handleInsert(context)
}
companion object {
@JvmStatic
fun createIfFrontendFriendly(os: OverridableSpace): FrontendFriendlyOverridableSpaceInsertHandler? {
// Try to convert tail type to frontend-friendly version
val tail = os.myTail
val ffTailType = tail as? FrontendFriendlyTailType ?: TailTypeSerializer.toDescriptor(tail) ?: return null
// Try to get delegate's effective handler
val delegateHandler = os.delegateEffectiveInsertHandler
val ffDelegate = if (delegateHandler != null) {
delegateHandler as? FrontendFriendlyInsertHandler ?: InsertHandlerSerializer.toDescriptor(delegateHandler)
?: return null // delegate handler must be frontend-friendly
}
else {
null // missing delegate handler is OK
}
return FrontendFriendlyOverridableSpaceInsertHandler(ffDelegate, ffTailType)
}
}
}
@@ -6,9 +6,10 @@ import com.intellij.codeInsight.TailTypes;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.TailTypeDecorator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public final class OverridableSpace extends TailTypeDecorator<LookupElement> {
private final @NotNull TailType myTail;
public final class OverridableSpace extends TailTypeDecorator<LookupElement> implements LookupElementWithEffectiveInsertHandler {
final @NotNull TailType myTail;
private OverridableSpace(@NotNull LookupElement keyword, @NotNull TailType tail) {
super(keyword);
@@ -20,6 +21,16 @@ public final class OverridableSpace extends TailTypeDecorator<LookupElement> {
return context.shouldAddCompletionChar() ? TailTypes.noneType() : myTail;
}
@Override
protected @Nullable InsertHandler<?> getDelegateEffectiveInsertHandler() {
return super.getDelegateEffectiveInsertHandler();
}
@Override
public @Nullable InsertHandler<?> getEffectiveInsertHandler() {
return FrontendFriendlyOverridableSpaceInsertHandler.createIfFrontendFriendly(this);
}
public static LookupElement create(@NotNull LookupElement delegate, @NotNull TailType tail) {
return new OverridableSpace(delegate, tail);
}