IDEA-57301: jsp: extract tag: support "introduce parameter" inside tag files
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / template / actions / SaveAsTemplateAction.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 /*
18  * Created by IntelliJ IDEA.
19  * User: mike
20  * Date: Aug 20, 2002
21  * Time: 5:04:04 PM
22  * To change template for new class use
23  * Code Style | Class Templates options (Tools | IDE Options).
24  */
25 package com.intellij.codeInsight.template.actions;
26
27 import com.intellij.codeInsight.CodeInsightBundle;
28 import com.intellij.codeInsight.template.TemplateContextType;
29 import com.intellij.codeInsight.template.impl.EditTemplateDialog;
30 import com.intellij.codeInsight.template.impl.TemplateImpl;
31 import com.intellij.codeInsight.template.impl.TemplateOptionalProcessor;
32 import com.intellij.codeInsight.template.impl.TemplateSettings;
33 import com.intellij.openapi.actionSystem.*;
34 import com.intellij.openapi.application.ApplicationManager;
35 import com.intellij.openapi.command.CommandProcessor;
36 import com.intellij.openapi.editor.Document;
37 import com.intellij.openapi.editor.Editor;
38 import com.intellij.openapi.editor.EditorFactory;
39 import com.intellij.openapi.editor.RangeMarker;
40 import com.intellij.openapi.extensions.Extensions;
41 import com.intellij.openapi.fileTypes.FileType;
42 import com.intellij.openapi.fileTypes.FileTypeManager;
43 import com.intellij.openapi.project.Project;
44 import com.intellij.openapi.util.TextRange;
45 import com.intellij.psi.*;
46 import com.intellij.psi.util.PsiElementFilter;
47 import com.intellij.psi.util.PsiTreeUtil;
48 import com.intellij.util.containers.HashMap;
49
50 import javax.swing.*;
51 import java.util.Map;
52
53 public class SaveAsTemplateAction extends AnAction {
54
55   public void actionPerformed(AnActionEvent e) {
56     DataContext dataContext = e.getDataContext();
57     final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
58     PsiFile file = LangDataKeys.PSI_FILE.getData(dataContext);
59
60     final Project project = file.getProject();
61     PsiDocumentManager.getInstance(project).commitAllDocuments();
62
63     final TextRange selection = new TextRange(editor.getSelectionModel().getSelectionStart(),
64                                               editor.getSelectionModel().getSelectionEnd());
65     PsiElement current = file.findElementAt(selection.getStartOffset());
66     int startOffset = selection.getStartOffset();
67     while (current instanceof PsiWhiteSpace) {
68       current = current.getNextSibling();
69       if (current == null) break;
70       startOffset = current.getTextRange().getStartOffset();
71     }
72
73     if (startOffset >= selection.getEndOffset()) startOffset = selection.getStartOffset();
74
75     final PsiElement[] psiElements = PsiTreeUtil.collectElements(file, new PsiElementFilter() {
76       public boolean isAccepted(PsiElement element) {
77         return selection.contains(element.getTextRange()) && element.getReferences().length > 0;
78       }
79     });
80
81     final Document document = EditorFactory.getInstance().createDocument(editor.getDocument().getText().
82                                                                          substring(startOffset,
83                                                                                    selection.getEndOffset()));
84     final int offsetDelta = startOffset;
85     CommandProcessor.getInstance().executeCommand(project, new Runnable() {
86       public void run() {
87         ApplicationManager.getApplication().runWriteAction(new Runnable() {
88           public void run() {
89             Map<RangeMarker, String> rangeToText = new HashMap<RangeMarker, String>();
90
91             for (PsiElement element : psiElements) {
92               for (PsiReference reference : element.getReferences()) {
93                 if (!(reference instanceof PsiQualifiedReference) || ((PsiQualifiedReference) reference).getQualifier() == null) {
94                   String canonicalText = reference.getCanonicalText();
95                   TextRange referenceRange = reference.getRangeInElement();
96                   TextRange range = element.getTextRange().cutOut(referenceRange).shiftRight(-offsetDelta);
97                   final String oldText = range.substring(document.getText());
98                   if (!canonicalText.equals(oldText)) {
99                     rangeToText.put(document.createRangeMarker(range), canonicalText);
100                   }
101                 }
102               }
103             }
104
105             for (Map.Entry<RangeMarker, String> entry : rangeToText.entrySet()) {
106               document.replaceString(entry.getKey().getStartOffset(), entry.getKey().getEndOffset(), entry.getValue());
107             }
108           }
109         });
110       }
111     }, null, null);
112
113     TemplateSettings templateSettings = TemplateSettings.getInstance();
114
115     TemplateImpl template = new TemplateImpl("", document.getText(), TemplateSettings.USER_GROUP_NAME);
116
117     FileType fileType = FileTypeManager.getInstance().getFileTypeByFile(file.getVirtualFile());
118     for(TemplateContextType contextType: Extensions.getExtensions(TemplateContextType.EP_NAME)) {
119       template.getTemplateContext().setEnabled(contextType, contextType.isInContext(fileType));
120     }
121
122     if (editTemplate(template, editor.getComponent(), true)) return;
123     templateSettings.addTemplate(template);
124   }
125
126   public static boolean editTemplate(TemplateImpl template, JComponent component, final boolean newTemplate) {
127
128     TemplateSettings templateSettings = TemplateSettings.getInstance();
129     String defaultShortcut = "";
130     if (templateSettings.getDefaultShortcutChar() == TemplateSettings.ENTER_CHAR) {
131       defaultShortcut = CodeInsightBundle.message("template.shortcut.enter");
132     }
133     if (templateSettings.getDefaultShortcutChar() == TemplateSettings.TAB_CHAR) {
134       defaultShortcut = CodeInsightBundle.message("template.shortcut.tab");
135     }
136     if (templateSettings.getDefaultShortcutChar() == TemplateSettings.SPACE_CHAR) {
137       defaultShortcut = CodeInsightBundle.message("template.shortcut.space");
138     }
139
140     Map<TemplateOptionalProcessor, Boolean> options = template.createOptions();
141     Map<TemplateContextType, Boolean> context = template.createContext();
142
143     EditTemplateDialog dialog = new EditTemplateDialog(
144       component,
145       CodeInsightBundle.message("dialog.edit.live.template.title"),
146       template,
147       templateSettings.getTemplateGroups(),
148       defaultShortcut, options, context, newTemplate);
149     dialog.show();
150     if (!dialog.isOK()) {
151       return true;
152     }
153     dialog.apply();
154     template.applyOptions(options);
155     template.applyContext(context);
156     templateSettings.setLastSelectedTemplateKey(template.getKey());
157     return false;
158   }
159
160   public void update(AnActionEvent e) {
161     DataContext dataContext = e.getDataContext();
162     Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
163     PsiFile file = LangDataKeys.PSI_FILE.getData(dataContext);
164
165     if (file == null || editor == null) {
166       e.getPresentation().setEnabled(false);
167     }
168     else {
169       e.getPresentation().setEnabled(editor.getSelectionModel().hasSelection());
170     }
171   }
172 }