| /* |
| * 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. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.codeInsight.template; |
| |
| 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.registry.Registry; |
| 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.*; |
| |
| /** |
| * @author Eugene.Kudelevsky |
| */ |
| 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> myVariableOccurrences = 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; |
| } |
| |
| public CharSequence getText() { |
| return myText; |
| } |
| |
| public static boolean isEndVariable(@NotNull String name) { |
| return name.startsWith(END_PREFIX); |
| } |
| |
| private static class VarOccurence { |
| String myName; |
| int myOffset; |
| |
| private VarOccurence(String name, int offset) { |
| myName = name; |
| myOffset = offset; |
| } |
| } |
| |
| public boolean findVarOccurence(String name) { |
| for (VarOccurence occurence : myVariableOccurrences) { |
| if (occurence.myName.equals(name)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @NotNull |
| public TemplateImpl buildTemplate() { |
| List<Variable> variables = getListWithLimit(myVariables); |
| if (!findVarOccurence(TemplateImpl.END)) { |
| if (myLastEndVarName == null) { |
| for (Variable variable : variables) { |
| if (isEndVariable(variable.getName())) { |
| myLastEndVarName = variable.getName(); |
| break; |
| } |
| } |
| } |
| if (myLastEndVarName != null) { |
| int endOffset = -1; |
| Iterator<VarOccurence> it = myVariableOccurrences.iterator(); |
| while (it.hasNext()) { |
| VarOccurence occurence = it.next(); |
| if (occurence.myName.equals(myLastEndVarName)) { |
| endOffset = occurence.myOffset; |
| break; |
| } |
| } |
| if (endOffset >= 0) { |
| for (Iterator<Variable> it1 = variables.iterator(); it1.hasNext(); ) { |
| Variable variable = it1.next(); |
| if (myLastEndVarName.equals(variable.getName()) && variable.isAlwaysStopAt()) { |
| it.remove(); |
| it1.remove(); |
| } |
| } |
| myVariableOccurrences.add(new VarOccurence(TemplateImpl.END, endOffset)); |
| } |
| } |
| } |
| TemplateImpl template = new TemplateImpl("", ""); |
| for (Variable variable : variables) { |
| template.addVariable(variable.getName(), variable.getExpressionString(), variable.getDefaultValueString(), variable.isAlwaysStopAt()); |
| } |
| |
| List<VarOccurence> variableOccurrences = getListWithLimit(myVariableOccurrences); |
| Collections.sort(variableOccurrences, new Comparator<VarOccurence>() { |
| @Override |
| public int compare(@NotNull VarOccurence o1, @NotNull VarOccurence o2) { |
| if (o1.myOffset < o2.myOffset) { |
| return -1; |
| } |
| if (o1.myOffset > o2.myOffset) { |
| return 1; |
| } |
| return 0; |
| } |
| }); |
| int last = 0; |
| for (VarOccurence occurence : variableOccurrences) { |
| template.addTextSegment(myText.substring(last, occurence.myOffset)); |
| template.addVariableSegment(occurence.myName); |
| last = occurence.myOffset; |
| } |
| template.addTextSegment(myText.substring(last)); |
| template.setToReformat(myIsToReformat); |
| return template; |
| } |
| |
| private <T> List<T> getListWithLimit(List<T> list) { |
| if (ApplicationManager.getApplication().isUnitTestMode()) { |
| return list; |
| } |
| if (mySegmentLimit == 0) { |
| return Collections.emptyList(); |
| } |
| if (mySegmentLimit > 0 && list.size() > mySegmentLimit) { |
| LOGGER.warn("Template with more than " + mySegmentLimit + " segments had been build (" + list.size() + "). Text: " + myText); |
| return list.subList(0, Math.min(list.size(), mySegmentLimit)); |
| } |
| return list; |
| } |
| |
| public void insertText(int offset, String text, boolean disableEndVariable) { |
| if (disableEndVariable) { |
| String varName = null; |
| for (VarOccurence occurence : myVariableOccurrences) { |
| if (!isEndVariable(occurence.myName)) { |
| continue; |
| } |
| if (occurence.myOffset == offset) { |
| varName = occurence.myName; |
| break; |
| } |
| } |
| if (varName != null) { |
| for (Variable variable : myVariables) { |
| if (varName.equals(variable.getName())) { |
| variable.setAlwaysStopAt(false); |
| variable.setDefaultValueString("\"\""); |
| break; |
| } |
| } |
| } |
| } |
| int delta = text.length(); |
| for (VarOccurence occurence : myVariableOccurrences) { |
| if (occurence.myOffset > offset || !disableEndVariable && occurence.myOffset == offset) { |
| occurence.myOffset += delta; |
| } |
| } |
| myText.insert(offset, text); |
| updateMarkers(offset, text); |
| } |
| |
| public int length() { |
| return myText.length(); |
| } |
| |
| private void updateMarkers(int offset, String text) { |
| for (Marker marker : myMarkers) { |
| if (offset < marker.getStartOffset()) { |
| marker.myStartOffset += text.length(); |
| } |
| else if (offset <= marker.getEndOffset()) { |
| marker.myEndOffset += text.length(); |
| } |
| } |
| } |
| |
| private String generateUniqueVarName(Set<String> existingNames, boolean end) { |
| String prefix = end ? END_PREFIX : "VAR"; |
| int i = 0; |
| while (myVarNames.contains(prefix + i) || existingNames.contains(prefix + i)) { |
| i++; |
| } |
| return prefix + i; |
| } |
| |
| public int insertTemplate(int offset, TemplateImpl template, Map<String, String> predefinedVarValues) { |
| myIsToReformat = myText.length() > 0 || template.isToReformat(); |
| removeEndVarAtOffset(offset); |
| |
| String text = template.getTemplateText(); |
| insertText(offset, text, false); |
| Map<String, String> newVarNames = new HashMap<String, String>(); |
| Set<String> oldVarNames = new HashSet<String>(); |
| for (int i = 0; i < template.getVariableCount(); i++) { |
| String varName = template.getVariableNameAt(i); |
| oldVarNames.add(varName); |
| } |
| for (int i = 0; i < template.getVariableCount(); i++) { |
| String varName = template.getVariableNameAt(i); |
| if (!TemplateImpl.INTERNAL_VARS_SET.contains(varName)) { |
| if (predefinedVarValues != null && predefinedVarValues.containsKey(varName)) { |
| continue; |
| } |
| String newVarName; |
| if (myVarNames.contains(varName)) { |
| oldVarNames.remove(varName); |
| newVarName = generateUniqueVarName(oldVarNames, isEndVariable(varName)); |
| newVarNames.put(varName, newVarName); |
| if (varName.equals(myLastEndVarName)) { |
| myLastEndVarName = newVarName; |
| } |
| } |
| else { |
| newVarName = varName; |
| } |
| Variable var = new Variable(newVarName, template.getExpressionStringAt(i), template.getDefaultValueStringAt(i), template.isAlwaysStopAt(i)); |
| if (mySegmentLimit >= 0 && myVariables.size() >= mySegmentLimit) { |
| if (mySegmentLimit > 0) { |
| LOGGER.warn("Template with more than " + mySegmentLimit + " segments had been build. Text: " + myText); |
| } |
| break; |
| } |
| myVariables.add(var); |
| myVarNames.add(newVarName); |
| } |
| } |
| int end = -1; |
| |
| for (int i = 0; i < template.getSegmentsCount(); i++) { |
| String segmentName = template.getSegmentName(i); |
| int localOffset = template.getSegmentOffset(i); |
| if (TemplateImpl.END.equals(segmentName)) { |
| end = offset + localOffset; |
| } |
| else { |
| if (predefinedVarValues != null && predefinedVarValues.containsKey(segmentName)) { |
| String value = predefinedVarValues.get(segmentName); |
| insertText(offset + localOffset, value, false); |
| offset += value.length(); |
| continue; |
| } |
| if (newVarNames.containsKey(segmentName)) { |
| segmentName = newVarNames.get(segmentName); |
| } |
| myVariableOccurrences.add(new VarOccurence(segmentName, offset + localOffset)); |
| } |
| } |
| int endOffset = end >= 0 ? end : offset + text.length(); |
| if (endOffset > 0 && |
| endOffset != offset + text.length() && |
| endOffset < myText.length() && |
| !hasVarAtOffset(endOffset)) { |
| myLastEndVarName = generateUniqueVarName(myVarNames, true); |
| myVariables.add(new Variable(myLastEndVarName, "", "", true)); |
| myVarNames.add(myLastEndVarName); |
| myVariableOccurrences.add(new VarOccurence(myLastEndVarName, endOffset)); |
| } |
| return endOffset; |
| } |
| |
| private void removeEndVarAtOffset(int offset) { |
| for (Iterator<VarOccurence> it = myVariableOccurrences.iterator(); it.hasNext();) { |
| VarOccurence occurence = it.next(); |
| if (!isEndVariable(occurence.myName)) { |
| continue; |
| } |
| if (occurence.myOffset == offset) { |
| it.remove(); |
| for (Iterator<Variable> it1 = myVariables.iterator(); it1.hasNext();) { |
| Variable variable = it1.next(); |
| if (occurence.myName.equals(variable.getName())) { |
| it1.remove(); |
| } |
| } |
| } |
| } |
| } |
| |
| private boolean hasVarAtOffset(int offset) { |
| boolean flag = false; |
| for (VarOccurence occurence : myVariableOccurrences) { |
| if (occurence.myOffset == offset) { |
| flag = true; |
| } |
| } |
| return flag; |
| } |
| |
| public Marker createMarker(int offset) { |
| Marker marker = new Marker(offset, offset); |
| myMarkers.add(marker); |
| return marker; |
| } |
| |
| public static class Marker { |
| int myStartOffset; |
| int myEndOffset; |
| |
| private Marker(int startOffset, int endOffset) { |
| myStartOffset = startOffset; |
| myEndOffset = endOffset; |
| } |
| |
| public int getStartOffset() { |
| return myStartOffset; |
| } |
| |
| public int getEndOffset() { |
| return myEndOffset; |
| } |
| } |
| } |