refactoring, zen coding shouldn't block live templates
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / template / CustomTemplateCallback.java
1 /*
2  * Copyright 2000-2010 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 package com.intellij.codeInsight.template;
17
18 import com.intellij.codeInsight.template.impl.TemplateImpl;
19 import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
20 import com.intellij.codeInsight.template.impl.TemplateSettings;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.editor.Editor;
23 import com.intellij.openapi.editor.RangeMarker;
24 import com.intellij.openapi.editor.ScrollType;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.psi.PsiFile;
27 import com.intellij.psi.codeStyle.CodeStyleManager;
28 import com.intellij.util.containers.HashMap;
29 import com.intellij.util.containers.HashSet;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
32
33 import java.util.*;
34
35 /**
36  * @author Eugene.Kudelevsky
37  */
38 public class CustomTemplateCallback {
39   private final TemplateManager myTemplateManager;
40   private final Editor myEditor;
41   private final PsiFile myFile;
42   private int myStartOffset;
43   private Project myProject;
44   private RangeMarker myGlobalMarker;
45   private RangeMarker myEndOffsetMarker;
46
47   //private final Map<Object, MyCheckpoint> myCheckpoints = new HashMap<Object, MyCheckpoint>();
48   private final Map<Object, RangeMarker> myCheckpoints = new HashMap<Object, RangeMarker>();
49
50   /*private static class MyCheckpoint {
51     int myFixedLength = null;
52     RangeMarker myFixedOffset;
53   }*/
54
55   public CustomTemplateCallback(Editor editor, PsiFile file) {
56     myEditor = editor;
57     myFile = file;
58     myProject = file.getProject();
59     myTemplateManager = TemplateManagerImpl.getInstance(myProject);
60   }
61
62   public void fixInitialEditorState() {
63     myStartOffset = myEditor.getCaretModel().getOffset();
64     myGlobalMarker = myEditor.getDocument().createRangeMarker(myStartOffset, myStartOffset);
65     myGlobalMarker.setGreedyToLeft(true);
66     myGlobalMarker.setGreedyToRight(true);
67   }
68
69   public void fixEndOffset() {
70     if (myEndOffsetMarker == null) {
71       int offset = myEditor.getCaretModel().getOffset();
72       myEndOffsetMarker = myEditor.getDocument().createRangeMarker(offset, offset);
73     }
74   }
75
76   public boolean isLiveTemplateApplicable(@NotNull String key) {
77     return findApplicableTemplate(key) != null;
78   }
79
80   @Nullable
81   public TemplateImpl findApplicableTemplate(@NotNull String key) {
82     List<TemplateImpl> templates = getMatchingTemplates(key);
83     templates = filterApplicableCandidates(templates);
84     return templates.size() > 0 ? templates.get(0) : null;
85   }
86
87   private List<TemplateImpl> filterApplicableCandidates(Collection<TemplateImpl> candidates) {
88     List<TemplateImpl> result = new ArrayList<TemplateImpl>();
89     for (TemplateImpl candidate : candidates) {
90       if (TemplateManagerImpl.isApplicable(myFile, myStartOffset, candidate)) {
91         result.add(candidate);
92       }
93     }
94     return result;
95   }
96
97   public boolean templateContainsVars(@NotNull String key, String... varNames) {
98     List<TemplateImpl> templates = getMatchingTemplates(key);
99     templates = filterApplicableCandidates(templates);
100     if (templates.size() == 0) {
101       return false;
102     }
103     TemplateImpl template = templates.get(0);
104     Set<String> varSet = new HashSet<String>();
105     for (int i = 0; i < template.getVariableCount(); i++) {
106       varSet.add(template.getVariableNameAt(i));
107     }
108     for (String varName : varNames) {
109       if (!varSet.contains(varName)) {
110         return false;
111       }
112     }
113     return true;
114   }
115
116   /**
117    * @param key
118    * @param predefinedVarValues
119    * @param listener            @return returns if template invokation is finished
120    */
121   public boolean startTemplate(@NotNull String key,
122                                Map<String, String> predefinedVarValues,
123                                @Nullable TemplateInvokationListener listener) {
124     //int caretOffset = myEditor.getCaretModel().getOffset();
125     List<TemplateImpl> templates = getMatchingTemplates(key);
126     templates = filterApplicableCandidates(templates);
127     if (templates.size() > 0) {
128       TemplateImpl template = templates.get(0);
129       return startTemplate(template, predefinedVarValues, listener);
130     }
131     else if (listener != null) {
132       listener.finished(false);
133     }
134     return true;
135   }
136
137   /**
138    * @param template
139    * @param predefinedVarValues
140    * @param listener
141    * @return returns if template invokation is finished
142    */
143   public boolean startTemplate(@NotNull Template template,
144                                Map<String, String> predefinedVarValues,
145                                @Nullable final TemplateInvokationListener listener) {
146     final boolean[] templateEnded = new boolean[]{false};
147     final boolean[] templateFinished = new boolean[]{false};
148     myTemplateManager.startTemplate(myEditor, template, false, predefinedVarValues, new TemplateEditingAdapter() {
149       @Override
150       public void templateFinished(Template template, final boolean brokenOff) {
151         ApplicationManager.getApplication().runWriteAction(new Runnable() {
152           public void run() {
153             if (brokenOff) {
154               reformat();
155             }
156           }
157         });
158         if (brokenOff) return;
159         templateFinished[0] = true;
160         if (templateEnded[0] && listener != null) {
161           listener.finished(true);
162         }
163       }
164
165       @Override
166       public void waitingForInput(Template template) {
167         reformat();
168       }
169     });
170     templateEnded[0] = true;
171     if (templateFinished[0] && listener != null) {
172       listener.finished(false);
173     }
174     return templateFinished[0];
175   }
176
177   private void reformat() {
178     CodeStyleManager style = CodeStyleManager.getInstance(myProject);
179     style.reformatText(myFile, myGlobalMarker.getStartOffset(), myGlobalMarker.getEndOffset());
180   }
181
182   public void fixStartOfTemplate(@NotNull Object key) {
183     int offset = myEditor.getCaretModel().getOffset();
184     RangeMarker marker = myEditor.getDocument().createRangeMarker(offset, offset);
185     marker.setGreedyToLeft(true);
186     marker.setGreedyToRight(true);
187     myCheckpoints.put(key, marker);
188   }
189
190   public void gotoEndOfTemplate(@NotNull Object key) {
191     myEditor.getCaretModel().moveToOffset(getEndOfTemplate(key));
192   }
193
194   public int getEndOfTemplate(@NotNull Object key) {
195     RangeMarker marker = myCheckpoints.get(key);
196     if (marker == null) {
197       throw new IllegalArgumentException();
198     }
199     return marker.getEndOffset();
200   }
201
202   public int getStartOfTemplate(@NotNull Object key) {
203     RangeMarker marker = myCheckpoints.get(key);
204     if (marker == null) {
205       throw new IllegalArgumentException();
206     }
207     return marker.getStartOffset();
208   }
209
210   public void gotoEndOffset() {
211     if (myEndOffsetMarker != null) {
212       myEditor.getCaretModel().moveToOffset(myEndOffsetMarker.getStartOffset());
213     }
214   }
215
216   public void finish() {
217     myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
218     final CodeStyleManager style = CodeStyleManager.getInstance(myProject);
219     if (myGlobalMarker != null) {
220       style.reformatText(myFile, myGlobalMarker.getStartOffset(), myGlobalMarker.getEndOffset());
221     }
222     gotoEndOffset();
223   }
224
225   private static List<TemplateImpl> getMatchingTemplates(@NotNull String templateKey) {
226     TemplateSettings settings = TemplateSettings.getInstance();
227     return settings.collectMatchingCandidates(templateKey, settings.getDefaultShortcutChar(), false);
228   }
229
230   @NotNull
231   public Editor getEditor() {
232     return myEditor;
233   }
234
235   public int getOffset() {
236     return myEditor.getCaretModel().getOffset();
237   }
238
239   public PsiFile getFile() {
240     return myFile;
241   }
242 }