aed9467de684ede81b7a4b623d1c46ec0914ffeb
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / template / impl / ListTemplatesHandler.java
1 /*
2 * Copyright 2000-2009 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.CodeInsightActionHandler;
20 import com.intellij.codeInsight.CodeInsightBundle;
21 import com.intellij.codeInsight.completion.PlainPrefixMatcher;
22 import com.intellij.codeInsight.hint.HintManager;
23 import com.intellij.codeInsight.lookup.*;
24 import com.intellij.codeInsight.lookup.impl.LookupImpl;
25 import com.intellij.codeInsight.template.TemplateManager;
26 import com.intellij.featureStatistics.FeatureUsageTracker;
27 import com.intellij.openapi.application.Result;
28 import com.intellij.openapi.command.WriteCommandAction;
29 import com.intellij.openapi.editor.Document;
30 import com.intellij.openapi.editor.Editor;
31 import com.intellij.openapi.editor.ex.util.EditorUtil;
32 import com.intellij.openapi.fileEditor.FileDocumentManager;
33 import com.intellij.openapi.project.Project;
34 import com.intellij.psi.PsiDocumentManager;
35 import com.intellij.psi.PsiFile;
36 import com.intellij.util.containers.CollectionFactory;
37 import org.jetbrains.annotations.NotNull;
38 import org.jetbrains.annotations.Nullable;
39
40 import java.util.*;
41
42 public class ListTemplatesHandler implements CodeInsightActionHandler {
43   public void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull PsiFile file) {
44     if (!FileDocumentManager.getInstance().requestWriting(editor.getDocument(), project)) {
45       return;
46     }
47     EditorUtil.fillVirtualSpaceUntilCaret(editor);
48
49     PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
50     int offset = editor.getCaretModel().getOffset();
51     String prefix = getPrefix(editor.getDocument(), offset);
52
53     List<TemplateImpl> matchingTemplates = new ArrayList<TemplateImpl>();
54     ArrayList<TemplateImpl> applicableTemplates = SurroundWithTemplateHandler.getApplicableTemplates(editor, file, false);
55     for (TemplateImpl template : applicableTemplates) {
56       if (template.getKey().startsWith(prefix)) {
57         matchingTemplates.add(template);
58       }
59     }
60
61     if (matchingTemplates.isEmpty()) {
62       matchingTemplates.addAll(applicableTemplates);
63       prefix = "";
64     }
65
66     if (matchingTemplates.size() == 0) {
67       String text = prefix.length() == 0
68                     ? CodeInsightBundle.message("templates.no.defined")
69                     : CodeInsightBundle.message("templates.no.defined.with.prefix", prefix);
70       HintManager.getInstance().showErrorHint(editor, text);
71       return;
72     }
73
74     showTemplatesLookup(project, editor, prefix, matchingTemplates);
75   }
76
77   public static void showTemplatesLookup(final Project project, final Editor editor,
78                                          @NotNull String prefix, List<TemplateImpl> matchingTemplates) {
79
80     final LookupImpl lookup = (LookupImpl)LookupManager.getInstance(project).createLookup(editor, LookupElement.EMPTY_ARRAY, prefix, LookupArranger.DEFAULT);
81     for (TemplateImpl template : matchingTemplates) {
82       lookup.addItem(createTemplateElement(template), new PlainPrefixMatcher(prefix));
83     }
84     
85     showLookup(lookup, null);
86   }
87
88   private static LiveTemplateLookupElement createTemplateElement(final TemplateImpl template) {
89     return new LiveTemplateLookupElement(template, false) {
90       @Override
91       public Set<String> getAllLookupStrings() {
92         String description = template.getDescription();
93         if (description == null) {
94           return super.getAllLookupStrings();
95         }
96         return CollectionFactory.newSet(getLookupString(), description);
97       }
98     };
99   }
100
101   private static String computePrefix(TemplateImpl template, String argument) {
102     String key = template.getKey();
103     if (argument == null) {
104       return key;
105     }
106     if (key.length() > 0 && Character.isJavaIdentifierPart(key.charAt(key.length() - 1))) {
107       return key + ' ' + argument;
108     }
109     return key + argument;
110   }
111
112   public static void showTemplatesLookup(final Project project, final Editor editor, Map<TemplateImpl, String> template2Argument) {
113     final LookupImpl lookup = (LookupImpl)LookupManager.getInstance(project).createLookup(editor, LookupElement.EMPTY_ARRAY, "", LookupArranger.DEFAULT);
114     for (TemplateImpl template : template2Argument.keySet()) {
115       String prefix = computePrefix(template, template2Argument.get(template));
116       lookup.addItem(createTemplateElement(template), new PlainPrefixMatcher(prefix));
117     }
118
119     showLookup(lookup, template2Argument);
120   }
121
122   private static void showLookup(LookupImpl lookup, @Nullable Map<TemplateImpl, String> template2Argument) {
123     Editor editor = lookup.getEditor();
124     Project project = editor.getProject();
125     lookup.addLookupListener(new MyLookupAdapter(project, editor, template2Argument));
126     lookup.refreshUi(false);
127     lookup.showLookup();
128   }
129
130   public boolean startInWriteAction() {
131     return true;
132   }
133
134   public static String getPrefix(Document document, int offset) {
135     CharSequence chars = document.getCharsSequence();
136     int start = offset;
137     while (true) {
138       if (start == 0) break;
139       char c = chars.charAt(start - 1);
140       if (!isInPrefix(c)) break;
141       start--;
142     }
143     return chars.subSequence(start, offset).toString();
144   }
145
146   private static boolean isInPrefix(final char c) {
147     return Character.isJavaIdentifierPart(c) || c == '.';
148   }
149
150   private static class MyLookupAdapter extends LookupAdapter {
151     private final Project myProject;
152     private final Editor myEditor;
153     private final Map<TemplateImpl, String> myTemplate2Argument;
154
155     public MyLookupAdapter(Project project, Editor editor, Map<TemplateImpl, String> template2Argument) {
156       myProject = project;
157       myEditor = editor;
158       myTemplate2Argument = template2Argument;
159     }
160
161     public void itemSelected(LookupEvent event) {
162       FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.liveTemplates");
163       LookupElement item = event.getItem();
164       if (item instanceof LiveTemplateLookupElement) {
165         final TemplateImpl template = ((LiveTemplateLookupElement)item).getTemplate();
166         final String argument = myTemplate2Argument != null ? myTemplate2Argument.get(template) : null;
167         new WriteCommandAction(myProject) {
168           protected void run(Result result) throws Throwable {
169             ((TemplateManagerImpl)TemplateManager.getInstance(myProject)).startTemplateWithPrefix(myEditor, template, null, argument);
170           }
171         }.execute();
172       }
173     }
174   }
175 }