From b333197cd306db67abde2eef8e48f1350e4722f6 Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Mon, 5 May 2014 21:33:15 +0400 Subject: [PATCH] Emmet: performance improvements, add segments limit. --- .../template/LiveTemplateBuilder.java | 65 +++++++++---------- .../src/misc/registry.properties | 1 + .../emmet/EmmetPreviewTypedHandler.java | 2 +- .../template/emmet/ZenCodingTemplate.java | 17 ++--- .../template/emmet/nodes/GenerationNode.java | 19 +++--- 5 files changed, 50 insertions(+), 54 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 592b6915cd36..50c193c404d2 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/LiveTemplateBuilder.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/LiveTemplateBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package com.intellij.codeInsight.template; import com.intellij.codeInsight.template.impl.TemplateImpl; import com.intellij.codeInsight.template.impl.Variable; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.registry.Registry; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.NonNls; @@ -29,15 +31,27 @@ import java.util.*; */ public class LiveTemplateBuilder { @NonNls private static final String END_PREFIX = "____END"; + private static final Logger LOGGER = Logger.getInstance(LiveTemplateBuilder.class); private final StringBuilder myText = new StringBuilder(); private final List myVariables = new ArrayList(); private final Set myVarNames = new HashSet(); private final List myVariableOccurences = new ArrayList(); private final List myMarkers = new ArrayList(); + private final int mySegmentLimit; private String myLastEndVarName; private boolean myIsToReformat = false; + + @SuppressWarnings("UnusedDeclaration") + public LiveTemplateBuilder() { + this(Registry.intValue("emmet.segments.limit")); + } + + public LiveTemplateBuilder(int segmentLimit) { + mySegmentLimit = segmentLimit; + } + public void setIsToReformat(boolean isToReformat) { myIsToReformat = isToReformat; } @@ -71,9 +85,10 @@ public class LiveTemplateBuilder { @NotNull public TemplateImpl buildTemplate() { + List variables = getListWithLimit(myVariables); if (!findVarOccurence(TemplateImpl.END)) { if (myLastEndVarName == null) { - for (Variable variable : myVariables) { + for (Variable variable : variables) { if (isEndVariable(variable.getName())) { myLastEndVarName = variable.getName(); break; @@ -91,7 +106,7 @@ public class LiveTemplateBuilder { } } if (endOffset >= 0) { - for (Iterator it1 = myVariables.iterator(); it1.hasNext();) { + for (Iterator it1 = variables.iterator(); it1.hasNext(); ) { Variable variable = it1.next(); if (myLastEndVarName.equals(variable.getName()) && variable.isAlwaysStopAt()) { it.remove(); @@ -103,10 +118,12 @@ public class LiveTemplateBuilder { } } TemplateImpl template = new TemplateImpl("", ""); - for (Variable variable : myVariables) { + for (Variable variable : variables) { template.addVariable(variable.getName(), variable.getExpressionString(), variable.getDefaultValueString(), variable.isAlwaysStopAt()); } - Collections.sort(myVariableOccurences, new Comparator() { + + List variableOccurrences = getListWithLimit(myVariableOccurences); + Collections.sort(variableOccurrences, new Comparator() { @Override public int compare(VarOccurence o1, VarOccurence o2) { if (o1.myOffset < o2.myOffset) { @@ -119,7 +136,7 @@ public class LiveTemplateBuilder { } }); int last = 0; - for (VarOccurence occurence : myVariableOccurences) { + for (VarOccurence occurence : variableOccurrences) { template.addTextSegment(myText.substring(last, occurence.myOffset)); template.addVariableSegment(occurence.myName); last = occurence.myOffset; @@ -129,25 +146,13 @@ public class LiveTemplateBuilder { return template; } - /*private void addEndPlaceholders() { - int[] endOffsets = myEndOffsets.toArray(); - Arrays.sort(endOffsets); - for (int i = 0, n = endOffsets.length; i < n; i++) { - int offset = endOffsets[i]; - if (offset < 0 || myText.length() == 0 || offset == myText.length() - 1 || hasVarAtOffset(offset)) { - continue; - } - if (i < n - 1) { - String varName = generateUniqueVarName(myVarNames); - myVarNames.add(varName); - myVariables.add(new Variable(varName, "", "", true)); - myVariableOccurences.add(new VarOccurence(varName, offset)); - } - else { - insertVariableSegment(offset, TemplateImpl.END); - } + private List getListWithLimit(List list) { + if (mySegmentLimit > 0 && list.size() > mySegmentLimit) { + LOGGER.warn("Template with more than 100 segments had been build. Text: " + myText); + return list.subList(0, Math.min(list.size(), mySegmentLimit)); } - }*/ + return list; + } public void insertText(int offset, String text, boolean disableEndVariable) { if (disableEndVariable) { @@ -205,18 +210,6 @@ public class LiveTemplateBuilder { return prefix + i; } - /*private static String preslashQuotes(String s) { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == '"') { - builder.append('\\'); - } - builder.append(c); - } - return builder.toString(); - }*/ - public int insertTemplate(int offset, TemplateImpl template, Map predefinedVarValues) { myIsToReformat = myText.length() > 0 || template.isToReformat(); removeEndVarAtOffset(offset); diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties index b0bc0b959d97..1a518eed66ce 100644 --- a/platform/platform-resources-en/src/misc/registry.properties +++ b/platform/platform-resources-en/src/misc/registry.properties @@ -398,3 +398,4 @@ focus.fix.lost.cursor.description=See IDEA-79312 ide.inertial.mouse.fix=false ide.inertial.mouse.fix.description=See IDEA-71508 +emmet.segments.limit=50 diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetPreviewTypedHandler.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetPreviewTypedHandler.java index 231916c47bd2..f65bf8ed3115 100644 --- a/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetPreviewTypedHandler.java +++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetPreviewTypedHandler.java @@ -112,7 +112,7 @@ public class EmmetPreviewTypedHandler extends TypedHandlerDelegate { if (!regularTemplateWithSamePrefixExists) { // exclude perfect matches with existing templates because LiveTemplateCompletionContributor handles it - ZenCodingTemplate.expand(templatePrefix, callback, null, generator, Collections.emptyList(), false); + ZenCodingTemplate.expand(templatePrefix, callback, null, generator, Collections.emptyList(), false, 0); TemplateImpl template = generatedTemplate.get(); String templateText = template != null ? template.getTemplateText() : null; if (!StringUtil.isEmpty(templateText)) { diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/ZenCodingTemplate.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/ZenCodingTemplate.java index 5b519a61de41..56e0bf2a137e 100644 --- a/xml/impl/src/com/intellij/codeInsight/template/emmet/ZenCodingTemplate.java +++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/ZenCodingTemplate.java @@ -48,6 +48,7 @@ import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.patterns.StandardPatterns; @@ -125,7 +126,7 @@ public class ZenCodingTemplate extends CustomLiveTemplateBase { public void expand(@NotNull String key, @NotNull CustomTemplateCallback callback) { ZenCodingGenerator defaultGenerator = findApplicableDefaultGenerator(callback.getContext(), false); assert defaultGenerator != null; - expand(key, callback, null, defaultGenerator, Collections.emptyList(), true); + expand(key, callback, null, defaultGenerator, Collections.emptyList(), true, Registry.intValue("emmet.segments.limit")); } @Nullable @@ -183,7 +184,7 @@ public class ZenCodingTemplate extends CustomLiveTemplateBase { public static void expand(@NotNull String key, @NotNull CustomTemplateCallback callback, @Nullable String surroundedText, @NotNull ZenCodingGenerator defaultGenerator, @NotNull Collection extraFilters, - boolean expandPrimitiveAbbreviations) { + boolean expandPrimitiveAbbreviations, int segmentsLimit) { final ZenCodingNode node = parse(key, callback, defaultGenerator, surroundedText); if (node == null) { return; @@ -211,14 +212,14 @@ public class ZenCodingTemplate extends CustomLiveTemplateBase { // commit is required. otherwise injections placed after caret will be broken PsiDocumentManager.getInstance(callback.getProject()).commitDocument(callback.getEditor().getDocument()); } - expand(node, generator, filters, surroundedText, callback, expandPrimitiveAbbreviations); + expand(node, generator, filters, surroundedText, callback, expandPrimitiveAbbreviations, segmentsLimit); } private static void expand(ZenCodingNode node, ZenCodingGenerator generator, List filters, String surroundedText, - CustomTemplateCallback callback, boolean expandPrimitiveAbbreviations) { + CustomTemplateCallback callback, boolean expandPrimitiveAbbreviations, int segmentsLimit) { if (surroundedText != null) { surroundedText = surroundedText.trim(); } @@ -233,11 +234,11 @@ public class ZenCodingTemplate extends CustomLiveTemplateBase { } List genNodes = fakeParentNode.getChildren(); - LiveTemplateBuilder builder = new LiveTemplateBuilder(); + LiveTemplateBuilder builder = new LiveTemplateBuilder(segmentsLimit); int end = -1; for (int i = 0, genNodesSize = genNodes.size(); i < genNodesSize; i++) { GenerationNode genNode = genNodes.get(i); - TemplateImpl template = genNode.generate(callback, generator, filters, true); + TemplateImpl template = genNode.generate(callback, generator, filters, true, segmentsLimit); int e = builder.insertTemplate(builder.length(), template, null); if (i < genNodesSize - 1 && genNode.isInsertNewLineBetweenNodes()) { builder.insertText(e, "\n", false); @@ -435,7 +436,7 @@ public class ZenCodingTemplate extends CustomLiveTemplateBase { EditorModificationUtil.deleteSelectedText(callback.getEditor()); PsiDocumentManager.getInstance(callback.getProject()).commitAllDocuments(); - expand(node, generator, filters, selection, callback, true); + expand(node, generator, filters, selection, callback, true, Registry.intValue("emmet.segments.limit")); } } }); @@ -504,7 +505,7 @@ public class ZenCodingTemplate extends CustomLiveTemplateBase { if (!regularTemplateWithSamePrefixExists) { // exclude perfect matches with existing templates because LiveTemplateCompletionContributor handles it final Collection extraFilters = ContainerUtil.newLinkedList(new SingleLineEmmetFilter()); - expand(templatePrefix, callback, null, generator, extraFilters, false); + expand(templatePrefix, callback, null, generator, extraFilters, false, 0); if (!generatedTemplate.isNull()) { final TemplateImpl template = generatedTemplate.get(); template.setKey(templatePrefix); diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/GenerationNode.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/GenerationNode.java index f0b1e49004fb..9a9c0177e699 100644 --- a/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/GenerationNode.java +++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/GenerationNode.java @@ -134,14 +134,14 @@ public class GenerationNode extends UserDataHolderBase { @NotNull public TemplateImpl generate(@NotNull CustomTemplateCallback callback, - @Nullable ZenCodingGenerator generator, - @NotNull Collection filters, - boolean insertSurroundedText) { + @Nullable ZenCodingGenerator generator, + @NotNull Collection filters, + boolean insertSurroundedText, int segmentsLimit) { myContainsSurroundedTextMarker = !(insertSurroundedText && myInsertSurroundedTextAtTheEnd); GenerationNode generationNode = this; if (generationNode != this) { - return generationNode.generate(callback, generator, Collections.emptyList(), insertSurroundedText); + return generationNode.generate(callback, generator, Collections.emptyList(), insertSurroundedText, segmentsLimit); } boolean shouldNotReformatTemplate = false; @@ -179,7 +179,7 @@ public class GenerationNode extends UserDataHolderBase { indentStr = StringUtil.repeatSymbol(' ', tabSize); } - LiveTemplateBuilder builder = new LiveTemplateBuilder(); + LiveTemplateBuilder builder = new LiveTemplateBuilder(segmentsLimit); int end = -1; boolean hasChildren = myChildren.size() > 0; @@ -204,7 +204,7 @@ public class GenerationNode extends UserDataHolderBase { parentTemplate.setString(s); final String txt = hasChildren || myContainsSurroundedTextMarker ? null : mySurroundedText; - parentTemplate = expandTemplate(parentTemplate, predefinedValues, txt); + parentTemplate = expandTemplate(parentTemplate, predefinedValues, txt, segmentsLimit); int offset = builder.insertTemplate(0, parentTemplate, null); int newOffset = gotoChild(callback.getProject(), builder.getText(), offset, 0, builder.length()); @@ -220,7 +220,7 @@ public class GenerationNode extends UserDataHolderBase { //noinspection ForLoopReplaceableByForEach for (int i = 0, myChildrenSize = myChildren.size(); i < myChildrenSize; i++) { GenerationNode child = myChildren.get(i); - TemplateImpl childTemplate = child.generate(callback, generator, filters, !myContainsSurroundedTextMarker); + TemplateImpl childTemplate = child.generate(callback, generator, filters, !myContainsSurroundedTextMarker, segmentsLimit); boolean blockTag = child.isBlockTag(); @@ -305,8 +305,9 @@ public class GenerationNode extends UserDataHolderBase { @NotNull private static TemplateImpl expandTemplate(@NotNull TemplateImpl template, Map predefinedVarValues, - String surroundedText) { - LiveTemplateBuilder builder = new LiveTemplateBuilder(); + String surroundedText, + int segmentsLimit) { + LiveTemplateBuilder builder = new LiveTemplateBuilder(segmentsLimit); if (predefinedVarValues == null && surroundedText == null) { return template; }