2 * Copyright 2000-2010 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.
16 package com.intellij.refactoring.changeSignature;
18 import com.intellij.openapi.command.CommandProcessor;
19 import com.intellij.openapi.components.ProjectComponent;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.editor.Document;
22 import com.intellij.openapi.editor.EditorBundle;
23 import com.intellij.openapi.editor.EditorFactory;
24 import com.intellij.openapi.editor.actions.EditorActionUtil;
25 import com.intellij.openapi.editor.event.DocumentAdapter;
26 import com.intellij.openapi.editor.event.DocumentEvent;
27 import com.intellij.openapi.editor.event.EditorFactoryEvent;
28 import com.intellij.openapi.editor.event.EditorFactoryListener;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.util.Comparing;
31 import com.intellij.psi.*;
32 import com.intellij.util.containers.HashMap;
33 import org.jetbrains.annotations.NotNull;
41 public class ChangeSignatureGestureDetector extends PsiTreeChangeAdapter implements ProjectComponent, EditorFactoryListener {
42 private final Project myProject;
43 private final Map<PsiFile, MyDocumentChangeAdapter> myListenerMap = new HashMap<PsiFile, MyDocumentChangeAdapter>();
44 private static final Logger LOG = Logger.getInstance("#" + ChangeSignatureGestureDetector.class.getName());
45 private boolean myDeaf = false;
47 public ChangeSignatureGestureDetector(Project project) {
51 public static ChangeSignatureGestureDetector getInstance(Project project){
52 return project.getComponent(ChangeSignatureGestureDetector.class);
55 public boolean isChangeSignatureAvailable(@NotNull PsiElement element) {
56 final MyDocumentChangeAdapter adapter = myListenerMap.get(element.getContainingFile());
57 if (adapter != null && adapter.getCurrentInfo() != null) {
58 final LanguageChangeSignatureDetector detector = LanguageChangeSignatureDetectors.INSTANCE.forLanguage(element.getLanguage());
59 LOG.assertTrue(detector != null);
60 return detector.isChangeSignatureAvailable(element, adapter.getCurrentInfo());
65 public boolean containsChangeSignatureChange(@NotNull PsiFile file) {
66 final MyDocumentChangeAdapter adapter = myListenerMap.get(file);
67 return adapter != null && adapter.getCurrentInfo() != null;
70 public void changeSignature(PsiFile file) {
73 final MyDocumentChangeAdapter changeBean = myListenerMap.get(file);
74 final ChangeInfo currentInfo = changeBean.getCurrentInfo();
75 final LanguageChangeSignatureDetector detector = LanguageChangeSignatureDetectors.INSTANCE.forLanguage(currentInfo.getLanguage());
76 if (detector.showDialog(currentInfo, changeBean.getInitialText())) {
86 public void projectOpened() {
87 PsiManager.getInstance(myProject).addPsiTreeChangeListener(this);
88 EditorFactory.getInstance().addEditorFactoryListener(this);
92 public void projectClosed() {
93 final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
94 for (PsiFile file : myListenerMap.keySet()) {
95 final MyDocumentChangeAdapter adapter = myListenerMap.get(file);
96 if (adapter != null) {
97 final Document document = documentManager.getDocument(file);
98 if (document != null) {
99 document.removeDocumentListener(adapter);
103 myListenerMap.clear();
105 PsiManager.getInstance(myProject).removePsiTreeChangeListener(this);
106 EditorFactory.getInstance().removeEditorFactoryListener(this);
111 public String getComponentName() {
112 return "ChangeSignatureGestureDetector";
116 public void initComponent() {
120 public void disposeComponent() {
124 public void childRemoved(PsiTreeChangeEvent event) {
125 change(event.getParent());
129 public void childReplaced(PsiTreeChangeEvent event) {
130 change(event.getChild());
134 public void childAdded(PsiTreeChangeEvent event) {
135 change(event.getChild());
138 private void change(PsiElement child) {
140 if (child == null) return;
141 final PsiFile file = child.getContainingFile();
143 final MyDocumentChangeAdapter changeBean = myListenerMap.get(file);
144 if (changeBean != null && changeBean.getInitialText() != null) {
145 final ChangeInfo info = LanguageChangeSignatureDetectors.createCurrentChangeInfo(child, changeBean.getInitialChangeInfo());
148 } else if (!info.equals(changeBean.getInitialChangeInfo())) {
149 changeBean.setCurrentInfo(info);
156 public void editorCreated(EditorFactoryEvent event) {
157 addDocListener(event.getEditor().getDocument());
160 public void addDocListener(Document document) {
161 final PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
163 final MyDocumentChangeAdapter adapter = new MyDocumentChangeAdapter();
164 document.addDocumentListener(adapter);
165 myListenerMap.put(file, adapter);
170 public void editorReleased(EditorFactoryEvent event) {
171 removeDocListener(event.getEditor().getDocument());
174 public void removeDocListener(Document document) {
175 final PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
176 final MyDocumentChangeAdapter adapter = myListenerMap.remove(file);
177 if (adapter != null) {
178 document.removeDocumentListener(adapter);
182 public void clearSignatureChange(PsiFile file) {
183 final MyDocumentChangeAdapter adapter = myListenerMap.get(file);
184 if (adapter != null) {
185 adapter.setBannedChangeInfo(adapter.getCurrentInfo());
190 private class MyDocumentChangeAdapter extends DocumentAdapter {
191 private String myInitialText;
192 private ChangeInfo myInitialChangeInfo;
193 private ChangeInfo myCurrentInfo;
194 private ChangeInfo myBannedChangeInfo;
196 public void setCurrentInfo(ChangeInfo currentInfo) {
197 myCurrentInfo = currentInfo;
200 public String getInitialText() {
201 return myInitialText;
204 public ChangeInfo getCurrentInfo() {
205 return myCurrentInfo;
209 public void beforeDocumentChange(DocumentEvent e) {
211 if (myInitialText == null) {
212 final Document document = e.getDocument();
213 final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
214 if (!documentManager.isUncommited(document)) {
215 final CommandProcessor processor = CommandProcessor.getInstance();
216 final String currentCommandName = processor.getCurrentCommandName();
217 if (!Comparing.strEqual(EditorBundle.message("typing.in.editor.command.name"), currentCommandName) &&
218 !Comparing.strEqual(EditorBundle.message("paste.command.name"), currentCommandName) &&
219 !Comparing.strEqual(LanguageChangeSignatureDetector.MOVE_PARAMETER, currentCommandName) &&
220 !Comparing.equal(EditorActionUtil.DELETE_COMMAND_GROUP, processor.getCurrentCommandGroupId())) {
223 final PsiFile file = documentManager.getPsiFile(document);
225 final PsiElement element = file.findElementAt(e.getOffset());
226 if (element != null) {
227 if (myBannedChangeInfo != null && LanguageChangeSignatureDetectors.wasBanned(element, myBannedChangeInfo)) return;
228 myBannedChangeInfo = null;
229 final ChangeInfo info = LanguageChangeSignatureDetectors.createCurrentChangeInfo(element, myCurrentInfo);
231 myInitialText = document.getText();
232 myInitialChangeInfo = info;
240 public ChangeInfo getInitialChangeInfo() {
241 return myInitialChangeInfo;
244 public void setBannedChangeInfo(ChangeInfo bannedChangeInfo) {
245 myBannedChangeInfo = bannedChangeInfo;
248 public void reinit() {
249 myInitialText = null;
250 myInitialChangeInfo = null;
251 myCurrentInfo = null;