2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.intellij.codeInsight.template.impl;
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;
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)) {
47 EditorUtil.fillVirtualSpaceUntilCaret(editor);
49 PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
50 int offset = editor.getCaretModel().getOffset();
51 String prefix = getPrefix(editor.getDocument(), offset);
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);
61 if (matchingTemplates.isEmpty()) {
62 matchingTemplates.addAll(applicableTemplates);
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);
74 Collections.sort(matchingTemplates, TemplateListPanel.TEMPLATE_COMPARATOR);
75 showTemplatesLookup(project, editor, prefix, matchingTemplates);
78 public static void showTemplatesLookup(final Project project, final Editor editor,
79 @NotNull String prefix, List<TemplateImpl> matchingTemplates) {
81 final LookupImpl lookup = (LookupImpl)LookupManager.getInstance(project).createLookup(editor, LookupElement.EMPTY_ARRAY, prefix, LookupArranger.DEFAULT);
82 lookup.setArranger(new LookupArranger() {
85 public Comparator<LookupElement> getItemComparator() {
86 return new Comparator<LookupElement>() {
88 public int compare(LookupElement o1, LookupElement o2) {
89 return o1.getLookupString().compareToIgnoreCase(o2.getLookupString());
96 public Classifier<LookupElement> createRelevanceClassifier() {
97 return new ComparingClassifier<LookupElement>(ClassifierFactory.<LookupElement>listClassifier(), "preferPrefix") {
100 public Comparable getWeight(LookupElement element) {
101 return !element.getLookupString().startsWith(lookup.itemPattern(element));
106 for (TemplateImpl template : matchingTemplates) {
107 lookup.addItem(createTemplateElement(template), new PlainPrefixMatcher(prefix));
110 showLookup(lookup, null);
113 private static LiveTemplateLookupElement createTemplateElement(final TemplateImpl template) {
114 return new LiveTemplateLookupElement(template, false) {
116 public Set<String> getAllLookupStrings() {
117 String description = template.getDescription();
118 if (description == null) {
119 return super.getAllLookupStrings();
121 return CollectionFactory.newSet(getLookupString(), description);
126 private static String computePrefix(TemplateImpl template, String argument) {
127 String key = template.getKey();
128 if (argument == null) {
131 if (key.length() > 0 && Character.isJavaIdentifierPart(key.charAt(key.length() - 1))) {
132 return key + ' ' + argument;
134 return key + argument;
137 public static void showTemplatesLookup(final Project project, final Editor editor, Map<TemplateImpl, String> template2Argument) {
138 final LookupImpl lookup = (LookupImpl)LookupManager.getInstance(project).createLookup(editor, LookupElement.EMPTY_ARRAY, "", LookupArranger.DEFAULT);
139 for (TemplateImpl template : template2Argument.keySet()) {
140 String prefix = computePrefix(template, template2Argument.get(template));
141 lookup.addItem(createTemplateElement(template), new PlainPrefixMatcher(prefix));
144 showLookup(lookup, template2Argument);
147 private static void showLookup(LookupImpl lookup, @Nullable Map<TemplateImpl, String> template2Argument) {
148 Editor editor = lookup.getEditor();
149 Project project = editor.getProject();
150 lookup.addLookupListener(new MyLookupAdapter(project, editor, template2Argument));
151 lookup.refreshUi(false);
155 public boolean startInWriteAction() {
159 public static String getPrefix(Document document, int offset) {
160 CharSequence chars = document.getCharsSequence();
163 if (start == 0) break;
164 char c = chars.charAt(start - 1);
165 if (!isInPrefix(c)) break;
168 return chars.subSequence(start, offset).toString();
171 private static boolean isInPrefix(final char c) {
172 return Character.isJavaIdentifierPart(c) || c == '.';
175 private static class MyLookupAdapter extends LookupAdapter {
176 private final Project myProject;
177 private final Editor myEditor;
178 private final Map<TemplateImpl, String> myTemplate2Argument;
180 public MyLookupAdapter(Project project, Editor editor, Map<TemplateImpl, String> template2Argument) {
183 myTemplate2Argument = template2Argument;
186 public void itemSelected(LookupEvent event) {
187 FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.liveTemplates");
188 LookupElement item = event.getItem();
189 if (item instanceof LiveTemplateLookupElement) {
190 final TemplateImpl template = ((LiveTemplateLookupElement)item).getTemplate();
191 final String argument = myTemplate2Argument != null ? myTemplate2Argument.get(template) : null;
192 new WriteCommandAction(myProject) {
193 protected void run(Result result) throws Throwable {
194 ((TemplateManagerImpl)TemplateManager.getInstance(myProject)).startTemplateWithPrefix(myEditor, template, null, argument);