From 1e970cc023bbfef6429956eefae5c94dc906587d Mon Sep 17 00:00:00 2001 From: "Irina.Chernushina" Date: Sat, 4 Nov 2017 17:10:08 +0100 Subject: [PATCH] emmet + live templates in tooltip preview: insert variables - insert default variable value or variable name, so that preview text make sense (and be highlighted correctly) - test for the case emmet + vue related to WEB-29541 --- .../template/LiveTemplateBuilder.java | 21 ++++++++++++++ .../template/emmet/EmmetPreviewHint.java | 7 ++++- .../template/emmet/EmmetPreviewUtil.java | 29 +++++++++++++++---- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/LiveTemplateBuilder.java b/platform/lang-impl/src/com/intellij/codeInsight/template/LiveTemplateBuilder.java index 5f28811983ca..4add3547a6a4 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/LiveTemplateBuilder.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/LiveTemplateBuilder.java @@ -19,13 +19,17 @@ import com.intellij.codeInsight.template.impl.TemplateImpl; import com.intellij.codeInsight.template.impl.Variable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; /** * @author Eugene.Kudelevsky @@ -63,6 +67,23 @@ public class LiveTemplateBuilder { return myText; } + @NotNull + public String getTextForPreview() { + if (myVariables.isEmpty()) return myText.toString(); + final StringBuilder sb = new StringBuilder(myText); + final Ref offset = new Ref<>(0); + final Map map = myVariables.stream().collect(Collectors.toMap(Variable::getName, Function.identity())); + myVariableOccurrences.forEach(vo -> { + final Variable variable = map.get(vo.myName); + if (variable == null) return; + String varReplacement = StringUtil.unquoteString(variable.getDefaultValueString()); + varReplacement = StringUtil.isEmptyOrSpaces(varReplacement) ? vo.myName : varReplacement; + sb.replace(vo.myOffset + offset.get(), vo.myOffset + offset.get(), varReplacement); + offset.set(offset.get() + varReplacement.length()); + }); + return sb.toString(); + } + public static boolean isEndVariable(@NotNull String name) { return name.startsWith(END_PREFIX); } diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetPreviewHint.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetPreviewHint.java index da08637f7106..090df5fbff9f 100644 --- a/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetPreviewHint.java +++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetPreviewHint.java @@ -39,6 +39,7 @@ import com.intellij.ui.LightweightHint; import com.intellij.ui.components.JBPanel; import com.intellij.util.Alarm; import com.intellij.util.DocumentUtil; +import com.intellij.util.ObjectUtils; import com.intellij.util.Producer; import com.intellij.util.ui.JBUI; import org.jetbrains.annotations.NotNull; @@ -53,13 +54,16 @@ public class EmmetPreviewHint extends LightweightHint implements Disposable { @NotNull private final Editor myParentEditor; @NotNull private final Editor myEditor; @NotNull private final Alarm myAlarm = new Alarm(this); - private boolean isDisposed = false; + private volatile boolean isDisposed = false; private EmmetPreviewHint(@NotNull JBPanel panel, @NotNull Editor editor, @NotNull Editor parentEditor) { super(panel); myParentEditor = parentEditor; myEditor = editor; + final Disposable parentDisposable = ObjectUtils.coalesce(ObjectUtils.tryCast(parentEditor, Disposable.class), parentEditor.getProject()); + Disposer.register(parentDisposable, this); + final Editor topLevelEditor = InjectedLanguageUtil.getTopLevelEditor(myParentEditor); EditorFactory.getInstance().addEditorFactoryListener(new EditorFactoryAdapter() { @Override @@ -199,6 +203,7 @@ public class EmmetPreviewHint extends LightweightHint implements Disposable { public void dispose() { isDisposed = true; myAlarm.cancelAllRequests(); + super.hide(); EmmetPreviewHint existingBalloon = myParentEditor.getUserData(KEY); if (existingBalloon == this) { myParentEditor.putUserData(KEY, null); diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetPreviewUtil.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetPreviewUtil.java index 31333f2f430c..ee7626a7cd56 100644 --- a/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetPreviewUtil.java +++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetPreviewUtil.java @@ -15,6 +15,7 @@ */ package com.intellij.codeInsight.template.emmet; +import com.intellij.codeInsight.template.LiveTemplateBuilder; import com.intellij.codeInsight.template.emmet.generators.XmlZenCodingGenerator; import com.intellij.codeInsight.template.emmet.generators.ZenCodingGenerator; import com.intellij.codeInsight.template.impl.TemplateImpl; @@ -26,6 +27,7 @@ import com.intellij.openapi.editor.event.CaretEvent; import com.intellij.openapi.editor.event.CaretListener; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.DocumentListener; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; @@ -51,12 +53,11 @@ public class EmmetPreviewUtil { final String templatePrefix = new ZenCodingTemplate().computeTemplateKeyWithoutContextChecking(callback); if (templatePrefix != null) { try { - ZenCodingTemplate.expand(templatePrefix, callback, generator, Collections.emptyList(), - expandPrimitiveAbbreviations, 0); - TemplateImpl template = callback.getGeneratedTemplate(); - String templateText = template != null ? template.getTemplateText() : null; - if (!StringUtil.isEmpty(templateText)) { - return template.isToReformat() ? reformatTemplateText(file, templateText) : templateText; + final int limit = Registry.intValue("emmet.segments.limit"); + ZenCodingTemplate.expand(templatePrefix, callback, generator, Collections.emptyList(), expandPrimitiveAbbreviations, limit); + final TemplateImpl template = callback.getGeneratedTemplate(); + if (template != null) { + return getFormattedText(template, file, limit); } } catch (EmmetException e) { @@ -68,6 +69,22 @@ public class EmmetPreviewUtil { return null; } + @Nullable + private static String getFormattedText(@NotNull final TemplateImpl template, @NotNull final PsiFile file, int limit) { + final String templateText; + if (template.getVariableCount() > 0 && template.getVariableCount() < limit/2) { + final LiveTemplateBuilder builder = new LiveTemplateBuilder(false, limit); + builder.insertTemplate(0, template, Collections.emptyMap()); + templateText = builder.getTextForPreview(); + } else { + templateText = template.getTemplateText(); + } + if (!StringUtil.isEmpty(templateText)) { + return template.isToReformat() ? reformatTemplateText(file, templateText) : templateText; + } + return null; + } + public static void addEmmetPreviewListeners(@NotNull final Editor editor, @NotNull final PsiFile file, final boolean expandPrimitiveAbbreviations) {