Emmet: performance improvements, add segments limit.

This commit is contained in:
Alexander Zolotov
2014-05-06 19:44:54 +04:00
parent 3d5fcbccc8
commit b333197cd3
5 changed files with 50 additions and 54 deletions
@@ -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<Variable> myVariables = new ArrayList<Variable>();
private final Set<String> myVarNames = new HashSet<String>();
private final List<VarOccurence> myVariableOccurences = new ArrayList<VarOccurence>();
private final List<Marker> myMarkers = new ArrayList<Marker>();
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<Variable> 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<Variable> it1 = myVariables.iterator(); it1.hasNext();) {
for (Iterator<Variable> 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<VarOccurence>() {
List<VarOccurence> variableOccurrences = getListWithLimit(myVariableOccurences);
Collections.sort(variableOccurrences, new Comparator<VarOccurence>() {
@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 <T> List<T> getListWithLimit(List<T> 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<String, String> predefinedVarValues) {
myIsToReformat = myText.length() > 0 || template.isToReformat();
removeEndVarAtOffset(offset);
@@ -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
@@ -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.<ZenCodingFilter>emptyList(), false);
ZenCodingTemplate.expand(templatePrefix, callback, null, generator, Collections.<ZenCodingFilter>emptyList(), false, 0);
TemplateImpl template = generatedTemplate.get();
String templateText = template != null ? template.getTemplateText() : null;
if (!StringUtil.isEmpty(templateText)) {
@@ -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.<ZenCodingFilter>emptyList(), true);
expand(key, callback, null, defaultGenerator, Collections.<ZenCodingFilter>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<? extends ZenCodingFilter> 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<ZenCodingFilter> 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<GenerationNode> 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<SingleLineEmmetFilter> 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);
@@ -134,14 +134,14 @@ public class GenerationNode extends UserDataHolderBase {
@NotNull
public TemplateImpl generate(@NotNull CustomTemplateCallback callback,
@Nullable ZenCodingGenerator generator,
@NotNull Collection<ZenCodingFilter> filters,
boolean insertSurroundedText) {
@Nullable ZenCodingGenerator generator,
@NotNull Collection<ZenCodingFilter> filters,
boolean insertSurroundedText, int segmentsLimit) {
myContainsSurroundedTextMarker = !(insertSurroundedText && myInsertSurroundedTextAtTheEnd);
GenerationNode generationNode = this;
if (generationNode != this) {
return generationNode.generate(callback, generator, Collections.<ZenCodingFilter>emptyList(), insertSurroundedText);
return generationNode.generate(callback, generator, Collections.<ZenCodingFilter>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<String, String> predefinedVarValues,
String surroundedText) {
LiveTemplateBuilder builder = new LiveTemplateBuilder();
String surroundedText,
int segmentsLimit) {
LiveTemplateBuilder builder = new LiveTemplateBuilder(segmentsLimit);
if (predefinedVarValues == null && surroundedText == null) {
return template;
}