4fc381b5506f38b8f205c886590b5ebed802b7aa
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / template / impl / TemplateImpl.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.codeInsight.template.impl;
18
19 import com.intellij.codeInsight.template.Expression;
20 import com.intellij.codeInsight.template.Template;
21 import com.intellij.openapi.extensions.Extensions;
22 import com.intellij.openapi.options.SchemeElement;
23 import com.intellij.openapi.util.text.StringUtil;
24 import com.intellij.psi.tree.IElementType;
25 import com.intellij.util.SmartList;
26 import com.intellij.util.containers.IntArrayList;
27 import org.jetbrains.annotations.NonNls;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
30
31 import java.util.*;
32
33 public class TemplateImpl extends Template implements SchemeElement {
34   private String myKey;
35   @NotNull private String myString;
36   private String myDescription;
37   private String myGroupName;
38   private char myShortcutChar = TemplateSettings.DEFAULT_CHAR;
39   private final List<Variable> myVariables = new SmartList<>();
40   private List<Segment> mySegments = null;
41   private String myTemplateText = null;
42   private String myId;
43
44   public boolean equals(Object o) {
45     if (this == o) return true;
46     if (!(o instanceof TemplateImpl)) return false;
47
48     final TemplateImpl template = (TemplateImpl) o;
49     if (myId != null && template.myId != null && myId.equals(template.myId)) return true;
50
51     if (isToReformat != template.isToReformat) return false;
52     if (isToShortenLongNames != template.isToShortenLongNames) return false;
53     if (myShortcutChar != template.myShortcutChar) return false;
54     if (myDescription != null ? !myDescription.equals(template.myDescription) : template.myDescription != null) return false;
55     if (myGroupName != null ? !myGroupName.equals(template.myGroupName) : template.myGroupName != null) return false;
56     if (myKey != null ? !myKey.equals(template.myKey) : template.myKey != null) return false;
57     if (!myString.equals(template.myString)) return false;
58     if (myTemplateText != null ? !myTemplateText.equals(template.myTemplateText) : template.myTemplateText != null) return false;
59
60     if (!new HashSet<>(myVariables).equals(new HashSet<>(template.myVariables))) return false;
61     if (isDeactivated != template.isDeactivated) return false;
62
63     return true;
64   }
65
66   public int hashCode() {
67     if (myId != null) {
68       return myId.hashCode();
69     }
70     int result;
71     result = myKey.hashCode();
72     result = 29 * result + myString.hashCode();
73     result = 29 * result + myGroupName.hashCode();
74     return result;
75   }
76
77   private boolean isToReformat = false;
78   private boolean isToShortenLongNames = true;
79   private boolean toParseSegments = true;
80   private TemplateContext myTemplateContext = new TemplateContext();
81
82   @NonNls public static final String END = "END";
83   @NonNls public static final String SELECTION = "SELECTION";
84   @NonNls public static final String SELECTION_START = "SELECTION_START";
85   @NonNls public static final String SELECTION_END = "SELECTION_END";
86   @NonNls public static final String ARG = "ARG";
87
88   public static final Set<String> INTERNAL_VARS_SET = new HashSet<>(Arrays.asList(
89     END, SELECTION, SELECTION_START, SELECTION_END));
90
91   private boolean isDeactivated = false;
92
93   public boolean isInline() {
94     return myIsInline;
95   }
96
97   private boolean isToIndent = true;
98
99
100   @Override
101   public void setInline(boolean isInline) {
102     myIsInline = isInline;
103   }
104
105   private boolean myIsInline = false;
106
107
108
109   public TemplateImpl(@NotNull String key, @NotNull String group) {
110     this(key, null, group);
111     toParseSegments = false;
112     myTemplateText = "";
113     mySegments = new SmartList<>();
114   }
115
116   public TemplateImpl(@NotNull String key, String string, @NotNull String group) {
117     myKey = key;
118     myString = StringUtil.convertLineSeparators(StringUtil.notNullize(string));
119     myGroupName = group;
120   }
121
122
123   @Override
124   public void addTextSegment(@NotNull String text) {
125     text = StringUtil.convertLineSeparators(text);
126     myTemplateText += text;
127   }
128
129   @Override
130   public void addVariableSegment(@NotNull String name) {
131     mySegments.add(new Segment(name, myTemplateText.length()));
132   }
133
134   @Override
135   public Variable addVariable(Expression expression, boolean isAlwaysStopAt) {
136     return addVariable("__Variable" + myVariables.size(), expression, isAlwaysStopAt);
137   }
138
139   @Override
140   public Variable addVariable(@NotNull String name,
141                               Expression expression,
142                               Expression defaultValueExpression,
143                               boolean isAlwaysStopAt,
144                               boolean skipOnStart) {
145     if (mySegments != null) {
146       Segment segment = new Segment(name, myTemplateText.length());
147       mySegments.add(segment);
148     }
149     Variable variable = new Variable(name, expression, defaultValueExpression, isAlwaysStopAt, skipOnStart);
150     myVariables.add(variable);
151     return variable;
152   }
153
154   @Override
155   public void addEndVariable() {
156     Segment segment = new Segment(END, myTemplateText.length());
157     mySegments.add(segment);
158   }
159
160   @Override
161   public void addSelectionStartVariable() {
162     Segment segment = new Segment(SELECTION_START, myTemplateText.length());
163     mySegments.add(segment);
164   }
165
166   @Override
167   public void addSelectionEndVariable() {
168     Segment segment = new Segment(SELECTION_END, myTemplateText.length());
169     mySegments.add(segment);
170   }
171
172   @Override
173   public String getId() {
174     return myId;
175   }
176
177   @NotNull
178   @Override
179   public TemplateImpl copy() {
180     TemplateImpl template = new TemplateImpl(myKey, myString, myGroupName);
181     template.resetFrom(this);
182     return template;
183   }
184
185   public void resetFrom(TemplateImpl another) {
186     removeAllParsed();
187     toParseSegments = another.toParseSegments;
188
189     myKey = another.getKey();
190     myString = another.myString;
191     myTemplateText = another.myTemplateText;
192     myGroupName = another.myGroupName;
193     myId = another.myId;
194     myDescription = another.myDescription;
195     myShortcutChar = another.myShortcutChar;
196     isToReformat = another.isToReformat;
197     isToShortenLongNames = another.isToShortenLongNames;
198     myIsInline = another.myIsInline;
199     myTemplateContext = another.myTemplateContext.createCopy();
200     isDeactivated = another.isDeactivated;
201     for (Property property : Property.values()) {
202       boolean value = another.getValue(property);
203       if (value != Template.getDefaultValue(property)) {
204         setValue(property, value);
205       }
206     }
207     for (Variable variable : another.myVariables) {
208       addVariable(variable.getName(), variable.getExpressionString(), variable.getDefaultValueString(), variable.isAlwaysStopAt());
209     }
210   }
211
212   @Override
213   public boolean isToReformat() {
214     return isToReformat;
215   }
216
217   @Override
218   public void setToReformat(boolean toReformat) {
219     isToReformat = toReformat;
220   }
221
222   @Override
223   public void setToIndent(boolean toIndent) {
224     isToIndent = toIndent;
225   }
226
227   public boolean isToIndent() {
228     return isToIndent;
229   }
230
231   @Override
232   public boolean isToShortenLongNames() {
233     return isToShortenLongNames;
234   }
235
236   @Override
237   public void setToShortenLongNames(boolean toShortenLongNames) {
238     isToShortenLongNames = toShortenLongNames;
239   }
240
241   public void setDeactivated(boolean isDeactivated) {
242     this.isDeactivated = isDeactivated;
243   }
244
245   public boolean isDeactivated() {
246     return isDeactivated;
247   }
248
249   @NotNull public TemplateContext getTemplateContext() {
250     return myTemplateContext;
251   }
252
253   public int getEndSegmentNumber() {
254     return getVariableSegmentNumber(END);
255   }
256
257   public int getSelectionStartSegmentNumber() {
258     return getVariableSegmentNumber(SELECTION_START);
259   }
260
261   public int getSelectionEndSegmentNumber() {
262     return getVariableSegmentNumber(SELECTION_END);
263   }
264
265   public int getVariableSegmentNumber(String variableName) {
266     parseSegments();
267     for (int i = 0; i < mySegments.size(); i++) {
268       Segment segment = mySegments.get(i);
269       if (segment.name.equals(variableName)) {
270         return i;
271       }
272     }
273     return -1;
274   }
275
276   public IntArrayList getVariableSegmentNumbers(String variableName) {
277     IntArrayList result = new IntArrayList();
278     parseSegments();
279     for (int i = 0; i < mySegments.size(); i++) {
280       Segment segment = mySegments.get(i);
281       if (segment.name.equals(variableName)) {
282         result.add(i);
283       }
284     }
285     return result;
286   }
287
288   @NotNull
289   @Override
290   public String getTemplateText() {
291     parseSegments();
292     return myTemplateText;
293   }
294
295   @Override
296   public String getSegmentName(int i) {
297     parseSegments();
298     return mySegments.get(i).name;
299   }
300
301   @Override
302   public int getSegmentOffset(int i) {
303     parseSegments();
304     return mySegments.get(i).offset;
305   }
306
307   @Override
308   public int getSegmentsCount() {
309     parseSegments();
310     return mySegments.size();
311   }
312
313   public void parseSegments() {
314     if(!toParseSegments) {
315       return;
316     }
317     if(mySegments != null) {
318       return;
319     }
320
321     mySegments = new SmartList<>();
322     StringBuilder buffer = new StringBuilder("");
323     TemplateTextLexer lexer = new TemplateTextLexer();
324     lexer.start(myString);
325
326     while(true){
327       IElementType tokenType = lexer.getTokenType();
328       if (tokenType == null) break;
329       int start = lexer.getTokenStart();
330       int end = lexer.getTokenEnd();
331       String token = myString.substring(start, end);
332       if (tokenType == TemplateTokenType.VARIABLE){
333         String name = token.substring(1, token.length() - 1);
334         Segment segment = new Segment(name, buffer.length());
335         mySegments.add(segment);
336       }
337       else if (tokenType == TemplateTokenType.ESCAPE_DOLLAR){
338         buffer.append("$");
339       }
340       else{
341         buffer.append(token);
342       }
343       lexer.advance();
344     }
345     myTemplateText = buffer.toString();
346   }
347
348   public void removeAllParsed() {
349     myVariables.clear();
350     mySegments = null;
351   }
352
353   @Override
354   public Variable addVariable(@NotNull String name, String expression, String defaultValue, boolean isAlwaysStopAt) {
355     Variable variable = new Variable(name, expression, defaultValue, isAlwaysStopAt);
356     myVariables.add(variable);
357     return variable;
358   }
359
360   public void removeVariable(int i) {
361     myVariables.remove(i);
362   }
363
364   public int getVariableCount() {
365     return myVariables.size();
366   }
367
368   public String getVariableNameAt(int i) {
369     return myVariables.get(i).getName();
370   }
371
372   public String getExpressionStringAt(int i) {
373     return myVariables.get(i).getExpressionString();
374   }
375
376   public Expression getExpressionAt(int i) {
377     return myVariables.get(i).getExpression();
378   }
379
380   public String getDefaultValueStringAt(int i) {
381     return myVariables.get(i).getDefaultValueString();
382   }
383
384   public Expression getDefaultValueAt(int i) {
385     return myVariables.get(i).getDefaultValueExpression();
386   }
387
388   public boolean isAlwaysStopAt(int i) {
389     return myVariables.get(i).isAlwaysStopAt();
390   }
391
392   @Override
393   public String getKey() {
394     return myKey;
395   }
396
397   public void setKey(String key) {
398     myKey = key;
399   }
400
401   @NotNull
402   @Override
403   public String getString() {
404     parseSegments();
405     return myString;
406   }
407
408   /**
409    * Set template text as it appears in Live Template settings, including variables surrounded with '$'.
410    * The text will be reparsed when needed.
411    * @param string template string text
412    */
413   public void setString(@NotNull String string) {
414     myString = StringUtil.convertLineSeparators(string);
415     toParseSegments = true;
416   }
417
418   @Override
419   public String getDescription() {
420     return myDescription;
421   }
422
423   public void setDescription(String description) {
424     myDescription = description;
425   }
426
427   public char getShortcutChar() {
428     return myShortcutChar;
429   }
430
431   public void setShortcutChar(char shortcutChar) {
432     myShortcutChar = shortcutChar;
433   }
434
435   public String getGroupName() {
436     return myGroupName;
437   }
438
439   @Override
440   public void setGroupName(@NotNull String groupName) {
441     myGroupName = groupName;
442   }
443
444   public boolean isSelectionTemplate() {
445     parseSegments();
446     for (Segment v : mySegments) {
447       if (SELECTION.equals(v.name)) return true;
448     }
449
450     return false;
451   }
452
453   public boolean hasArgument() {
454     for (Variable v : myVariables) {
455       if (v.getName().equals(ARG)) return true;
456     }
457     return false;
458   }
459
460   public void setId(@Nullable final String id) {
461     myId = id;
462   }
463
464   public Map<TemplateOptionalProcessor, Boolean> createOptions() {
465     Map<TemplateOptionalProcessor, Boolean> context = new LinkedHashMap<>();
466     for (TemplateOptionalProcessor processor : Extensions.getExtensions(TemplateOptionalProcessor.EP_NAME)) {
467       context.put(processor, processor.isEnabled(this));
468     }
469     return context;
470   }
471
472   public TemplateContext createContext() {
473     return getTemplateContext().createCopy();
474   }
475
476   public boolean contextsEqual(TemplateImpl defaultTemplate) {
477     return getTemplateContext().getDifference(defaultTemplate.getTemplateContext()) == null;
478   }
479
480   public void applyOptions(final Map<TemplateOptionalProcessor, Boolean> context) {
481     for (Map.Entry<TemplateOptionalProcessor, Boolean> entry : context.entrySet()) {
482       entry.getKey().setEnabled(this, entry.getValue().booleanValue());
483     }
484   }
485
486   public void applyContext(final TemplateContext context) {
487     myTemplateContext = context.createCopy();
488   }
489
490   public boolean skipOnStart(int i) {
491     return myVariables.get(i).skipOnStart();
492   }
493
494   public ArrayList<Variable> getVariables() {
495     return new ArrayList<>(myVariables);
496   }
497
498   private static class Segment {
499     public final String name;
500     public final int offset;
501
502     private Segment(@NotNull String name, int offset) {
503       this.name = name;
504       this.offset = offset;
505     }
506   }
507
508   @Override
509   public String toString() {
510     return myGroupName +"/" + myKey;
511   }
512 }