refactoring, CustomTemplateCallback#getContext() should work through injection
[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.lang.injection.InjectedLanguageManager;
22 import com.intellij.openapi.editor.Editor;
23 import com.intellij.openapi.fileTypes.FileType;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.psi.PsiElement;
26 import com.intellij.psi.PsiFile;
27 import com.intellij.psi.PsiFileFactory;
28 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
29 import com.intellij.util.LocalTimeCounter;
30 import com.intellij.util.containers.HashMap;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.List;
37 import java.util.Map;
38
39 /**
40  * @author Eugene.Kudelevsky
41  */
42 public class CustomTemplateCallback {
43   private final TemplateManager myTemplateManager;
44   private final Editor myEditor;
45   private final PsiFile myFile;
46   private int myStartOffset;
47   private final Project myProject;
48   private LiveTemplateBuilder.Marker myEndOffsetMarker;
49   private final Map<Object, LiveTemplateBuilder.Marker> myCheckpoints = new HashMap<Object, LiveTemplateBuilder.Marker>();
50
51   private FileType myFileType;
52
53   private LiveTemplateBuilder myBuilder = new LiveTemplateBuilder();
54   private int myOffset = 0;
55
56   public CustomTemplateCallback(Editor editor, PsiFile file) {
57     myEditor = editor;
58     myFile = file;
59     myProject = file.getProject();
60     myTemplateManager = TemplateManagerImpl.getInstance(myProject);
61     fixInitialState();
62   }
63
64   @Nullable
65   public PsiElement getContext() {
66     return getContext(myFile, myStartOffset > 0 ? myStartOffset - 1 : myStartOffset);
67   }
68
69   public void fixInitialState() {
70     myStartOffset = myEditor.getCaretModel().getOffset();
71   }
72
73   public void fixEndOffset() {
74     if (myEndOffsetMarker == null) {
75       myEndOffsetMarker = myBuilder.createMarker(myOffset);
76     }
77   }
78
79   public boolean isLiveTemplateApplicable(@NotNull String key) {
80     return findApplicableTemplate(key) != null;
81   }
82
83   @Nullable
84   public TemplateImpl findApplicableTemplate(@NotNull String key) {
85     List<TemplateImpl> templates = findApplicableTemplates(key);
86     return templates.size() > 0 ? templates.get(0) : null;
87   }
88
89   @NotNull
90   public List<TemplateImpl> findApplicableTemplates(String key) {
91     List<TemplateImpl> templates = getMatchingTemplates(key);
92     templates = filterApplicableCandidates(templates);
93     return templates;
94   }
95
96   private List<TemplateImpl> filterApplicableCandidates(Collection<TemplateImpl> candidates) {
97     List<TemplateImpl> result = new ArrayList<TemplateImpl>();
98     for (TemplateImpl candidate : candidates) {
99       if (TemplateManagerImpl.isApplicable(myFile, myStartOffset, candidate)) {
100         result.add(candidate);
101       }
102     }
103     return result;
104   }
105
106   /**
107    * @param key
108    * @param predefinedVarValues
109    * @param listener            @return returns if template invokation is finished
110    */
111   public void expandTemplate(@NotNull String key,
112                              Map<String, String> predefinedVarValues) {
113     List<TemplateImpl> templates = findApplicableTemplates(key);
114     if (templates.size() > 0) {
115       TemplateImpl template = templates.get(0);
116       expandTemplate(template, predefinedVarValues);
117     }
118   }
119
120   public void expandTemplate(@NotNull TemplateImpl template,
121                              Map<String, String> predefinedVarValues) {
122     int offset = myBuilder.insertTemplate(myOffset, template, predefinedVarValues);
123     moveToOffset(offset);
124   }
125
126   public void fixStartOfTemplate(@NotNull Object key) {
127     LiveTemplateBuilder.Marker marker = myBuilder.createMarker(myOffset);
128     myCheckpoints.put(key, marker);
129   }
130
131   public void gotoEndOfTemplate(@NotNull Object key) {
132     moveToOffset(getEndOfTemplate(key));
133   }
134
135   public int getEndOfTemplate(@NotNull Object key) {
136     LiveTemplateBuilder.Marker marker = myCheckpoints.get(key);
137     if (marker == null) {
138       throw new IllegalArgumentException();
139     }
140     return marker.getEndOffset();
141   }
142
143   public int getStartOfTemplate(@NotNull Object key) {
144     LiveTemplateBuilder.Marker marker = myCheckpoints.get(key);
145     if (marker == null) {
146       throw new IllegalArgumentException();
147     }
148     return marker.getStartOffset();
149   }
150
151   public void gotoEndOffset() {
152     if (myEndOffsetMarker != null) {
153       moveToOffset(myEndOffsetMarker.getStartOffset());
154     }
155   }
156
157   public void startAllExpandedTemplates() {
158     /*myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
159     final CodeStyleManager style = CodeStyleManager.getInstance(myProject);
160     if (myGlobalMarker != null) {
161       style.reformatText(myFile, myGlobalMarker.getStartOffset(), myGlobalMarker.getEndOffset());
162     }*/
163     if (myBuilder.getText().length() == 0) {
164       return;
165     }
166     gotoEndOffset();
167     if (myOffset < myBuilder.getText().length()) {
168       myBuilder.insertVariableSegment(myOffset, TemplateImpl.END);
169     }
170     TemplateImpl template = myBuilder.buildTemplate();
171     myTemplateManager.startTemplate(myEditor, template, false, myBuilder.getPredefinedValues(), null);
172     myBuilder = new LiveTemplateBuilder();
173     myEndOffsetMarker = null;
174     myCheckpoints.clear();
175   }
176
177   public boolean startTemplate() {
178     Map<TemplateImpl, String> template2Argument =
179       ((TemplateManagerImpl)myTemplateManager).findMatchingTemplates(myFile, myEditor, null, TemplateSettings.getInstance());
180     return ((TemplateManagerImpl)myTemplateManager).startNonCustomTemplates(template2Argument, myEditor, null);
181   }
182
183   private static List<TemplateImpl> getMatchingTemplates(@NotNull String templateKey) {
184     TemplateSettings settings = TemplateSettings.getInstance();
185     List<TemplateImpl> candidates = new ArrayList<TemplateImpl>();
186     for (TemplateImpl template : settings.getTemplates(templateKey)) {
187       if (!template.isDeactivated() && !template.isSelectionTemplate()) {
188         candidates.add(template);
189       }
190     }
191     return candidates;
192   }
193
194   @NotNull
195   public Editor getEditor() {
196     return myEditor;
197   }
198
199   @NotNull
200   public FileType getFileType() {
201     if (myFileType == null) {
202       myFileType = myFile.getFileType();
203     }
204     return myFileType;
205   }
206
207   public int getOffset() {
208     return myOffset;
209   }
210
211   @NotNull
212   public PsiFile parseCurrentText(FileType fileType) {
213     return PsiFileFactory.getInstance(myProject)
214       .createFileFromText("dummy.xml", fileType, myBuilder.getText(), LocalTimeCounter.currentTime(), false);
215   }
216
217   public Project getProject() {
218     return myProject;
219   }
220
221   public void moveToOffset(int offset) {
222     myOffset = offset;
223   }
224
225   public void insertString(int offset, String text) {
226     myBuilder.insertText(offset, text);
227   }
228
229   public void deleteTemplateKey(String key) {
230     int caretAt = myEditor.getCaretModel().getOffset();
231     myEditor.getDocument().deleteString(caretAt - key.length(), caretAt);
232   }
233
234   public static PsiElement getContext(PsiFile file, int offset) {
235     PsiElement element = null;
236     if (!InjectedLanguageManager.getInstance(file.getProject()).isInjectedFragment(file)) {
237       element = InjectedLanguageUtil.findInjectedElementNoCommit(file, offset);
238     }
239     if (element == null) {
240       element = file.findElementAt(offset > 0 ? offset - 1 : offset);
241       if (element == null) {
242         element = file;
243       }
244     }
245     return element;
246   }
247 }