IJPL-233853 [mod-completion] LiveTemplateCompletionContributor (2st step)

GitOrigin-RevId: eb7a5d5e65a307bb01f9883451196fac0c0e53ea
This commit is contained in:
Tagir Valeev
2026-02-11 15:35:19 +00:00
committed by intellij-monorepo-bot
parent e6c2be9ab0
commit 3eee71e033
10 changed files with 85 additions and 20 deletions
@@ -26,6 +26,7 @@ import com.intellij.codeInsight.template.macro.MethodNameMacro;
import com.intellij.codeInsight.template.macro.MethodReturnTypeMacro;
import com.intellij.codeInsight.template.macro.VariableOfTypeMacro;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModCommand;
import com.intellij.modcommand.ModCommandExecutor;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.impl.DocumentImpl;
@@ -7,6 +7,7 @@ import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.completion.PrefixMatcher;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageExtension;
import com.intellij.lang.LanguageExtensionWithAny;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.PsiElement;
@@ -23,7 +24,7 @@ import java.util.function.Consumer;
*/
@NotNullByDefault
public interface ModCompletionItemProvider {
LanguageExtension<ModCompletionItemProvider> EP_NAME = new LanguageExtension<>("com.intellij.modcompletion.completionItemProvider");
LanguageExtension<ModCompletionItemProvider> EP_NAME = new LanguageExtensionWithAny<>("com.intellij.modcompletion.completionItemProvider");
/**
* Provide completion items for given context
@@ -4,8 +4,8 @@ package com.intellij.codeInsight.template.impl;
import com.intellij.codeInsight.template.Expression;
import com.intellij.codeInsight.template.Template;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModCommand;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.ModTemplateBuilder;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.RangeMarker;
@@ -397,8 +397,10 @@ public class TemplateImpl extends TemplateBase implements SchemeElement {
}
/**
* @param context context where the template should be executed
* @return the {@link ModCommand}, which when executed will show the template like the current one.
* Performs a template execution within ModCommand context. The template is not actually executed, but
* contributes to {@link ModPsiUpdater} to form the final {@link ModCommand}.
*
* @param updater {@link ModPsiUpdater} to use.
*/
@ApiStatus.Internal
public void update(@NotNull ModPsiUpdater updater, @NotNull TemplateStateProcessor processor) {
@@ -4798,7 +4798,6 @@ f:com.intellij.codeInsight.template.impl.InvokeTemplateAction
f:com.intellij.codeInsight.template.impl.ListTemplatesHandler
- com.intellij.codeInsight.CodeInsightActionHandler
- <init>():V
- s:filterTemplatesByPrefix(java.util.Collection,com.intellij.openapi.editor.Editor,I,Z,Z):java.util.Map
- s:getCustomTemplatesLookupItems(com.intellij.openapi.editor.Editor,com.intellij.psi.PsiFile,I):com.intellij.util.containers.MultiMap
- invoke(com.intellij.openapi.project.Project,com.intellij.openapi.editor.Editor,com.intellij.psi.PsiFile):V
- s:showTemplatesLookup(com.intellij.openapi.project.Project,com.intellij.openapi.editor.Editor,java.util.Map):V
+2
View File
@@ -408,6 +408,8 @@ com.intellij.codeInsight.template.impl.DefaultLiveTemplatesProvider
- sf:EP_NAME:com.intellij.openapi.extensions.ExtensionPointName
- a:getDefaultLiveTemplateFiles():java.lang.String[]
- a:getHiddenLiveTemplateFiles():java.lang.String[]
f:com.intellij.codeInsight.template.impl.ListTemplatesHandler
- s:filterTemplatesByPrefix(java.util.Collection,com.intellij.openapi.editor.Document,I,Z,Z):java.util.Map
f:com.intellij.codeInsight.template.impl.TemplateManagerImpl
- s:updateTemplate(com.intellij.codeInsight.template.impl.TemplateImpl,com.intellij.modcommand.ModPsiUpdater):V
com.intellij.codeInsight.template.postfix.templates.PostfixTemplateExpressionSelector
@@ -56,12 +56,13 @@ public final class ListTemplatesHandler implements CodeInsightActionHandler {
public void invoke(final @NotNull Project project, final @NotNull Editor editor, @NotNull PsiFile psiFile) {
EditorUtil.fillVirtualSpaceUntilCaret(editor);
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
Document document = editor.getDocument();
PsiDocumentManager.getInstance(project).commitDocument(document);
int offset = editor.getCaretModel().getOffset();
List<TemplateImpl> applicableTemplates = TemplateManagerImpl.listApplicableTemplateWithInsertingDummyIdentifier(
TemplateActionContext.expanding(psiFile, editor));
Map<TemplateImpl, String> matchingTemplates = filterTemplatesByPrefix(applicableTemplates, editor, offset, false, true);
Map<TemplateImpl, String> matchingTemplates = filterTemplatesByPrefix(applicableTemplates, document, offset, false, true);
MultiMap<String, CustomLiveTemplateLookupElement> customTemplatesLookupElements = getCustomTemplatesLookupItems(editor, psiFile, offset);
if (matchingTemplates.isEmpty()) {
@@ -81,17 +82,16 @@ public final class ListTemplatesHandler implements CodeInsightActionHandler {
}
public static @NotNull Map<TemplateImpl, String> filterTemplatesByPrefix(@NotNull Collection<? extends TemplateImpl> templates,
@NotNull Editor editor,
int offset,
@NotNull Document document, int offset,
boolean fullMatch,
boolean searchInDescription) {
if (offset > editor.getDocument().getTextLength()) {
if (offset > document.getTextLength()) {
LOG.error("Cannot filter templates, index out of bounds. Offset: " + offset,
CoreAttachmentFactory.createAttachment(editor.getDocument()));
CoreAttachmentFactory.createAttachment(document));
}
CharSequence documentText = editor.getDocument().getCharsSequence().subSequence(0, offset);
CharSequence documentText = document.getCharsSequence().subSequence(0, offset);
String prefixWithoutDots = computeDescriptionMatchingPrefix(editor.getDocument(), offset);
String prefixWithoutDots = computeDescriptionMatchingPrefix(document, offset);
Pattern prefixSearchPattern = Pattern.compile(".*\\b" + prefixWithoutDots + ".*");
Map<TemplateImpl, String> matchingTemplates = new TreeMap<>(TemplateListPanel.TEMPLATE_COMPARATOR);
@@ -39,8 +39,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.intellij.codeInsight.template.impl.ListTemplatesHandler.filterTemplatesByPrefix;
public final class LiveTemplateCompletionContributor extends CompletionContributor implements DumbAware {
private static final Key<Boolean> ourShowTemplatesInTests = Key.create("ShowTemplatesInTests");
@@ -79,7 +77,8 @@ public final class LiveTemplateCompletionContributor extends CompletionContribut
Editor editor = parameters.getEditor();
int offset = editor.getCaretModel().getOffset();
List<TemplateImpl> availableTemplates = TemplateManagerImpl.listApplicableTemplates(TemplateActionContext.expanding(file, editor));
Map<TemplateImpl, String> templates = filterTemplatesByPrefix(availableTemplates, editor, offset, false, false);
Map<TemplateImpl, String> templates =
ListTemplatesHandler.filterTemplatesByPrefix(availableTemplates, editor.getDocument(), offset, false, false);
boolean isAutopopup = parameters.getInvocationCount() == 0;
AtomicBoolean templatesShown = new AtomicBoolean(false);
@@ -155,7 +154,8 @@ public final class LiveTemplateCompletionContributor extends CompletionContribut
public static @Nullable TemplateImpl findFullMatchedApplicableTemplate(@NotNull Editor editor,
int offset,
@NotNull Collection<? extends TemplateImpl> availableTemplates) {
Map<TemplateImpl, String> templates = filterTemplatesByPrefix(availableTemplates, editor, offset, true, false);
Map<TemplateImpl, String> templates =
ListTemplatesHandler.filterTemplatesByPrefix(availableTemplates, editor.getDocument(), offset, true, false);
if (templates.size() == 1) {
TemplateImpl template = ContainerUtil.getFirstItem(templates.keySet());
if (template != null) {
@@ -0,0 +1,56 @@
// 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.impl;
import com.intellij.codeInsight.template.TemplateActionContext;
import com.intellij.icons.AllIcons;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcompletion.ModCompletionItem;
import com.intellij.modcompletion.ModCompletionItemPresentation;
import com.intellij.modcompletion.ModCompletionItemProvider;
import com.intellij.modcompletion.PsiUpdateCompletionItem;
import com.intellij.openapi.util.text.MarkupText;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNullByDefault;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@NotNullByDefault
final class LiveTemplateModCompletionItemProvider implements ModCompletionItemProvider {
@Override
public void provideItems(CompletionContext context, Consumer<ModCompletionItem> sink) {
if (!LiveTemplateCompletionContributor.shouldShowAllTemplates()) return;
PsiFile file = context.getPosition().getContainingFile();
int offset = context.offset();
List<TemplateImpl> availableTemplates = TemplateManagerImpl.listApplicableTemplates(
TemplateActionContext.expanding(file, offset));
Map<TemplateImpl, String> templates =
ListTemplatesHandler.filterTemplatesByPrefix(availableTemplates, file.getFileDocument(), offset, false, false);
for (Map.Entry<TemplateImpl, String> entry : templates.entrySet()) {
sink.accept(new LiveTemplateModCompletionItem(entry.getKey()));
}
}
private static class LiveTemplateModCompletionItem extends PsiUpdateCompletionItem<TemplateImpl> {
private LiveTemplateModCompletionItem(TemplateImpl template) {
super(template.getKey(), template);
}
@Override
public void update(ActionContext actionContext, InsertionContext insertionContext, ModPsiUpdater updater) {
updater.getDocument().deleteString(actionContext.selection().getStartOffset(), actionContext.selection().getEndOffset());
TemplateManagerImpl.updateTemplate(contextObject(), updater);
}
@Override
public ModCompletionItemPresentation presentation() {
String description = contextObject().getDescription();
return new ModCompletionItemPresentation(MarkupText.plainText(contextObject().getKey()))
.withMainIcon(() -> AllIcons.Nodes.Template)
.withDetailText(description == null ? MarkupText.empty() : MarkupText.plainText(description));
}
}
}
@@ -64,8 +64,6 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import static com.intellij.codeInsight.template.impl.ListTemplatesHandler.filterTemplatesByPrefix;
public final class TemplateManagerImpl extends TemplateManager implements Disposable {
private final @NotNull Project myProject;
private static final Key<Boolean> ourTemplateTesting = Key.create("TemplateTesting");
@@ -599,7 +597,9 @@ public final class TemplateManagerImpl extends TemplateManager implements Dispos
public static boolean isApplicableTemplatePresent(@NotNull PsiFile file, @NotNull Editor editor) {
TemplateActionContext context = TemplateActionContext.expanding(file, editor);
int offset = editor.getCaretModel().getOffset();
Map<TemplateImpl, String> templates = filterTemplatesByPrefix(listApplicableTemplates(context), editor, offset, true, false);
@NotNull Collection<? extends TemplateImpl> applicableTemplates = listApplicableTemplates(context);
Map<TemplateImpl, String> templates =
ListTemplatesHandler.filterTemplatesByPrefix(applicableTemplates, editor.getDocument(), offset, true, false);
if (!templates.isEmpty()) {
return true;
}
@@ -676,6 +676,10 @@
implementationClass="com.intellij.codeInsight.completion.ReferenceBasedCompletionContributor"/>
<completion.contributor language="any" id="commands" order="first, after liveTemplates"
implementationClass="com.intellij.codeInsight.completion.command.CommandCompletionContributor"/>
<modcompletion.completionItemProvider language="any" implementationClass="com.intellij.codeInsight.template.impl.LiveTemplateModCompletionItemProvider"
id="liveTemplates" order="first"/>
<codeCompletionConfigurable provider="com.intellij.codeInsight.completion.command.configuration.CommandCompletionConfigurableProvider"/>
<applicationService serviceImplementation="com.intellij.execution.console.ConsoleFoldingSettings"/>