update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / completion / DefaultInsertHandler.java
blobc664bb90d14a9406ce5764c1ab59713f78316bc2
1 /*
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.
16 package com.intellij.codeInsight.completion;
18 import com.intellij.codeInsight.*;
19 import com.intellij.codeInsight.generation.GenerateMembersUtil;
20 import com.intellij.codeInsight.generation.OverrideImplementUtil;
21 import com.intellij.codeInsight.generation.PsiGenerationInfo;
22 import com.intellij.codeInsight.generation.PsiMethodMember;
23 import com.intellij.codeInsight.lookup.Lookup;
24 import com.intellij.codeInsight.lookup.LookupElement;
25 import com.intellij.codeInsight.lookup.LookupItem;
26 import com.intellij.featureStatistics.FeatureUsageTracker;
27 import com.intellij.ide.util.MemberChooser;
28 import com.intellij.openapi.application.ApplicationManager;
29 import com.intellij.openapi.command.CommandProcessor;
30 import com.intellij.openapi.command.UndoConfirmationPolicy;
31 import com.intellij.openapi.diagnostic.Logger;
32 import com.intellij.openapi.editor.Document;
33 import com.intellij.openapi.editor.Editor;
34 import com.intellij.openapi.editor.RangeMarker;
35 import com.intellij.openapi.editor.ScrollType;
36 import com.intellij.openapi.editor.ex.EditorEx;
37 import com.intellij.openapi.editor.highlighter.HighlighterIterator;
38 import com.intellij.openapi.fileEditor.FileDocumentManager;
39 import com.intellij.openapi.project.Project;
40 import com.intellij.openapi.util.text.StringUtil;
41 import com.intellij.psi.*;
42 import com.intellij.psi.codeStyle.CodeStyleManager;
43 import com.intellij.psi.codeStyle.CodeStyleSettings;
44 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
45 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
46 import com.intellij.psi.infos.CandidateInfo;
47 import com.intellij.psi.util.PsiTreeUtil;
48 import com.intellij.psi.util.PsiUtil;
49 import com.intellij.util.IncorrectOperationException;
50 import org.jetbrains.annotations.NotNull;
52 import java.util.ArrayList;
53 import java.util.Collection;
54 import java.util.List;
56 public class DefaultInsertHandler extends TemplateInsertHandler implements Cloneable {
57 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.DefaultInsertHandler");
59 protected InsertionContext myContext;
60 private LookupItem<?> myLookupItem;
62 private Project myProject;
63 private PsiFile myFile;
64 private Editor myEditor;
65 protected Document myDocument;
66 private InsertHandlerState myState;
68 public void handleInsert(final InsertionContext context, LookupElement item) {
69 super.handleInsert(context, item);
71 handleInsertInner(context, (LookupItem)item, context.getCompletionChar());
74 private void clear() {
75 myEditor = null;
76 myDocument = null;
77 myProject = null;
78 myFile = null;
79 myState = null;
80 myLookupItem = null;
81 myContext = null;
84 private void handleInsertInner(InsertionContext context, LookupItem item, final char completionChar) {
85 LOG.assertTrue(CommandProcessor.getInstance().getCurrentCommand() != null);
86 PsiDocumentManager.getInstance(context.getProject()).commitDocument(context.getEditor().getDocument());
87 myContext = context;
88 myLookupItem = item;
90 myProject = myContext.getProject();
91 myFile = myContext.getFile();
92 myEditor = myContext.getEditor();
93 myDocument = myEditor.getDocument();
95 TailType tailType = getTailType(completionChar);
97 //adjustContextAfterLookupStringInsertion();
98 myState = new InsertHandlerState(myContext.getSelectionEndOffset(), myContext.getSelectionEndOffset());
100 final boolean needLeftParenth = isToInsertParenth();
101 final boolean hasParams = needLeftParenth && hasParams();
103 if (CompletionUtil.isOverwrite(item, completionChar))
104 removeEndOfIdentifier(needLeftParenth && hasParams);
105 else if(myContext.getOffsetMap().getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET) != myContext.getSelectionEndOffset())
106 JavaCompletionUtil.resetParensInfo(context.getOffsetMap());
108 handleParenses(hasParams, needLeftParenth, tailType);
109 handleBrackets();
111 if (myLookupItem.getObject() instanceof PsiVariable) {
112 if (completionChar == '!' && PsiType.BOOLEAN.isAssignableFrom(((PsiVariable) myLookupItem.getObject()).getType())) {
113 PsiDocumentManager.getInstance(myProject).commitDocument(myDocument);
114 final PsiReferenceExpression ref =
115 PsiTreeUtil.findElementOfClassAtOffset(myFile, myState.tailOffset - 1, PsiReferenceExpression.class, false);
116 if (ref != null) {
117 FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EXCLAMATION_FINISH);
118 myDocument.insertString(ref.getTextRange().getStartOffset(), "!");
119 myState.caretOffset++;
120 myState.tailOffset++;
125 RangeMarker saveMaker = null;
126 final boolean generateAnonymousBody = myLookupItem.getAttribute(LookupItem.GENERATE_ANONYMOUS_BODY_ATTR) != null;
127 if (generateAnonymousBody){
128 saveMaker = myDocument.createRangeMarker(myState.caretOffset, myState.caretOffset);
129 myDocument.insertString(myState.tailOffset, "{}");
130 myState.caretOffset = myState.tailOffset + 1;
131 myState.tailOffset += 2;
134 myContext.setTailOffset(myState.tailOffset);
135 myState.caretOffset = processTail(tailType, myState.caretOffset, myState.tailOffset);
136 myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
137 myEditor.getSelectionModel().removeSelection();
139 qualifyIfNeeded();
142 if (needLeftParenth && hasParams){
143 // Invoke parameters popup
144 AutoPopupController.getInstance(myProject).autoPopupParameterInfo(myEditor, null);
147 if (tailType == TailType.DOT){
148 AutoPopupController.getInstance(myProject).autoPopupMemberLookup(myEditor, null);
151 if (generateAnonymousBody) {
152 context.setLaterRunnable(generateAnonymousBody());
153 if (hasParams) {
154 int offset = saveMaker.getStartOffset();
155 myEditor.getCaretModel().moveToOffset(offset);
156 myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
157 myEditor.getSelectionModel().removeSelection();
159 return;
162 if (completionChar == '#') {
163 context.setLaterRunnable(new Runnable() {
164 public void run() {
165 new CodeCompletionHandlerBase(CompletionType.BASIC) {
166 }.invoke(myProject, myEditor, myFile);
171 if (insertingAnnotation()) {
172 // Check if someone inserts annotation class that require @
173 PsiElement elementAt = myFile.findElementAt(myContext.getStartOffset());
174 final PsiElement parentElement = elementAt != null ? elementAt.getParent():null;
176 if (elementAt instanceof PsiIdentifier &&
177 (PsiTreeUtil.getParentOfType(elementAt, PsiAnnotationParameterList.class) != null ||
178 parentElement instanceof PsiErrorElement && parentElement.getParent() instanceof PsiJavaFile // top level annotation without @
180 && isAtTokenNeeded()) {
181 int expectedOffsetForAtToken = elementAt.getTextRange().getStartOffset();
182 myDocument.insertString(expectedOffsetForAtToken, "@");
187 private void qualifyIfNeeded() {
188 try{
189 if (myLookupItem.getObject() instanceof PsiField) {
190 PsiDocumentManager.getInstance(myFile.getProject()).commitAllDocuments();
191 PsiReference reference = myFile.findReferenceAt(myContext.getStartOffset());
192 if (reference instanceof PsiReferenceExpression && !((PsiReferenceExpression) reference).isQualified()) {
193 final PsiField member = (PsiField)myLookupItem.getObject();
194 final PsiVariable target =
195 JavaPsiFacade.getInstance(myProject).getResolveHelper().resolveReferencedVariable(member.getName(), (PsiElement)reference);
196 if (member.getManager().areElementsEquivalent(target, JavaCompletionUtil.getOriginalElement(member))) return;
198 final PsiClass psiClass = member.getContainingClass();
199 if (psiClass != null && StringUtil.isNotEmpty(psiClass.getName())) {
200 myDocument.insertString(myContext.getStartOffset(), psiClass.getName() + ".");
204 addImportForItem(myFile, myContext.getStartOffset(), myLookupItem);
206 catch(IncorrectOperationException e){
207 LOG.error(e);
211 private boolean isAtTokenNeeded() {
212 HighlighterIterator iterator = ((EditorEx)myContext.getEditor()).getHighlighter().createIterator(myContext.getStartOffset());
213 LOG.assertTrue(iterator.getTokenType() == JavaTokenType.IDENTIFIER);
214 iterator.retreat();
215 if (iterator.getTokenType() == TokenType.WHITE_SPACE) iterator.retreat();
216 return iterator.getTokenType() != JavaTokenType.AT && iterator.getTokenType() != JavaTokenType.DOT;
219 private void handleBrackets(){
220 // brackets
221 final Integer bracketsAttr = (Integer)myLookupItem.getUserData(LookupItem.BRACKETS_COUNT_ATTR);
222 if (bracketsAttr != null){
223 int count = bracketsAttr.intValue();
224 if(count > 0)
225 myState.caretOffset = myState.tailOffset + 1;
226 for(int i = 0; i < count; i++){
227 myDocument.insertString(myState.tailOffset, "[]");
228 myState.tailOffset += 2;
233 private void handleParenses(final boolean hasParams, final boolean needParenth, TailType tailType){
234 final boolean generateAnonymousBody = myLookupItem.getAttribute(LookupItem.GENERATE_ANONYMOUS_BODY_ATTR) != null;
235 boolean insertRightParenth = tailType != TailType.SMART_COMPLETION;
237 if (needParenth){
238 if (myContext.getOffsetMap().getOffset(JavaCompletionUtil.LPAREN_OFFSET) >= 0 && myContext.getOffsetMap().getOffset(JavaCompletionUtil.ARG_LIST_END_OFFSET) >= 0){
239 myState.tailOffset = myContext.getOffsetMap().getOffset(JavaCompletionUtil.ARG_LIST_END_OFFSET);
240 if (myContext.getOffsetMap().getOffset(JavaCompletionUtil.RPAREN_OFFSET) < 0 && insertRightParenth){
241 myDocument.insertString(myState.tailOffset, ")");
242 myState.tailOffset += 1;
244 if (hasParams){
245 myState.caretOffset = myContext.getOffsetMap().getOffset(JavaCompletionUtil.LPAREN_OFFSET) + 1;
247 else{
248 myState.caretOffset = myContext.getOffsetMap().getOffset(JavaCompletionUtil.ARG_LIST_END_OFFSET);
251 else{
252 final CodeStyleSettings styleSettings = CodeStyleSettingsManager.getSettings(myProject);
253 myState.tailOffset = myContext.getSelectionEndOffset();
254 myState.caretOffset = myContext.getSelectionEndOffset();
256 if(styleSettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES){
257 myDocument.insertString(myState.tailOffset++, " ");
258 myState.caretOffset ++;
260 if (insertRightParenth) {
261 final CharSequence charsSequence = myDocument.getCharsSequence();
262 if (charsSequence.length() <= myState.tailOffset || charsSequence.charAt(myState.tailOffset) != '(') {
263 myDocument.insertString(myState.tailOffset, "(");
266 myDocument.insertString(myState.tailOffset + 1, ")");
267 if (hasParams){
268 myState.tailOffset += 2;
269 myState.caretOffset++;
271 else{
272 if (tailType != TailTypes.CALL_RPARENTH || generateAnonymousBody) {
273 myState.tailOffset += 2;
274 myState.caretOffset += 2;
276 else {
277 myState.tailOffset++;
278 myState.caretOffset++;
282 else{
283 myDocument.insertString(myState.tailOffset++, "(");
284 myState.caretOffset ++;
287 if(hasParams && styleSettings.SPACE_WITHIN_METHOD_CALL_PARENTHESES){
288 myDocument.insertString(myState.caretOffset++, " ");
289 myState.tailOffset++;
295 private boolean isToInsertParenth(){
296 boolean needParens = false;
297 if (myLookupItem.getAttribute(LookupItem.NEW_OBJECT_ATTR) != null){
298 PsiDocumentManager.getInstance(myProject).commitDocument(myDocument);
299 needParens = true;
300 final PsiClass aClass = (PsiClass)myLookupItem.getObject();
302 PsiElement place = myFile.findElementAt(myContext.getStartOffset());
304 if(myLookupItem.getAttribute(LookupItem.DONT_CHECK_FOR_INNERS) == null){
305 PsiClass[] classes = aClass.getInnerClasses();
306 for (PsiClass inner : classes) {
307 if (!inner.hasModifierProperty(PsiModifier.STATIC)) continue;
308 if (!JavaPsiFacade.getInstance(inner.getProject()).getResolveHelper().isAccessible(inner, place, null)) continue;
309 needParens = false;
310 break;
313 } else if (insertingAnnotationWithParameters()) {
314 needParens = true;
316 return needParens;
319 private boolean hasParams(){
320 boolean hasParms = false;
321 if (myLookupItem.getAttribute(LookupItem.NEW_OBJECT_ATTR) != null){
322 PsiDocumentManager.getInstance(myProject).commitDocument(myDocument);
323 final PsiClass aClass = (PsiClass)myLookupItem.getObject();
325 final PsiElement place = myFile.findElementAt(myContext.getStartOffset());
327 final PsiMethod[] constructors = aClass.getConstructors();
328 for (PsiMethod constructor : constructors) {
329 if (!JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper().isAccessible(constructor, place, null)) continue;
330 if (constructor.getParameterList().getParametersCount() > 0) {
331 hasParms = true;
332 break;
336 else {
337 final String lookupString = myLookupItem.getLookupString();
338 if (PsiKeyword.SYNCHRONIZED.equals(lookupString)) {
339 final PsiElement place = myFile.findElementAt(myContext.getStartOffset());
340 hasParms = PsiTreeUtil.getParentOfType(place, PsiMember.class, PsiCodeBlock.class) instanceof PsiCodeBlock;
342 else if(PsiKeyword.CATCH.equals(lookupString) ||
343 PsiKeyword.SWITCH.equals(lookupString) ||
344 PsiKeyword.WHILE.equals(lookupString) ||
345 PsiKeyword.FOR.equals(lookupString))
346 hasParms = true;
347 else if (insertingAnnotationWithParameters()) {
348 hasParms = true;
351 return hasParms;
354 private boolean insertingAnnotationWithParameters() {
355 if(insertingAnnotation()) {
356 final Document document = myContext.getEditor().getDocument();
357 PsiDocumentManager.getInstance(myContext.getProject()).commitDocument(document);
358 PsiElement elementAt = myFile.findElementAt(myContext.getStartOffset());
359 if (elementAt instanceof PsiIdentifier) {
360 final PsiModifierListOwner parent = PsiTreeUtil.getParentOfType(elementAt, PsiModifierListOwner.class, false, PsiCodeBlock.class);
361 if (parent != null) {
362 for (PsiMethod m : ((PsiClass)myLookupItem.getObject()).getMethods()) {
363 if (!(m instanceof PsiAnnotationMethod)) continue;
364 final PsiAnnotationMemberValue defaultValue = ((PsiAnnotationMethod)m).getDefaultValue();
365 if (defaultValue == null) return true;
370 return false;
373 private boolean insertingAnnotation() {
374 final Object obj = myLookupItem.getObject();
375 if (!(obj instanceof PsiClass) || !((PsiClass)obj).isAnnotationType()) return false;
377 final Document document = myEditor.getDocument();
378 PsiDocumentManager.getInstance(myFile.getProject()).commitDocument(document);
379 final int offset = myContext.getStartOffset();
381 if (PsiTreeUtil.findElementOfClassAtOffset(myFile, offset, PsiImportStatement.class, false) != null) return false;
383 //outside of any class: we are surely inserting an annotation
384 if (PsiTreeUtil.findElementOfClassAtOffset(myFile, offset, PsiClass.class, false) == null) return true;
386 //the easiest check that there's a @ before the identifier
387 return PsiTreeUtil.findElementOfClassAtOffset(myFile, offset, PsiAnnotation.class, false) != null;
391 protected void removeEndOfIdentifier(boolean needParenth){
392 JavaCompletionUtil.initOffsets(myContext.getFile(), myContext.getProject(), myContext.getOffsetMap());
393 myDocument.deleteString(myContext.getSelectionEndOffset(), myContext.getOffsetMap().getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET));
394 if(myContext.getOffsetMap().getOffset(JavaCompletionUtil.LPAREN_OFFSET) > 0 && !needParenth){
395 myDocument.deleteString(myContext.getOffsetMap().getOffset(JavaCompletionUtil.LPAREN_OFFSET),
396 myContext.getOffsetMap().getOffset(JavaCompletionUtil.ARG_LIST_END_OFFSET));
397 JavaCompletionUtil.resetParensInfo(myContext.getOffsetMap());
401 protected TailType getTailType(final char completionChar){
402 switch(completionChar){
403 case '.': return TailType.DOT;
404 case ',': return TailType.COMMA;
405 case ';': return TailType.SEMICOLON;
406 case '=': return TailType.EQ;
407 case ' ': return TailType.SPACE;
408 case ':': return TailType.CASE_COLON; //?
409 case '(': return TailTypeEx.SMART_LPARENTH;
410 case '<':
411 case '>':
412 case '#':
413 case '\"':
414 case '[': return TailType.createSimpleTailType(completionChar);
415 case Lookup.COMPLETE_STATEMENT_SELECT_CHAR: return TailType.SMART_COMPLETION;
416 //case '!': if (!(myLookupItem.getObject() instanceof PsiVariable)) return TailType.EXCLAMATION;
418 final TailType attr = myLookupItem.getTailType();
419 return attr == TailType.UNKNOWN ? TailType.NONE : attr;
422 private int processTail(TailType tailType, int caretOffset, int tailOffset) {
423 myEditor.getCaretModel().moveToOffset(caretOffset);
424 tailType.processTail(myEditor, tailOffset);
425 return myEditor.getCaretModel().getOffset();
428 private Runnable generateAnonymousBody() {
429 PsiDocumentManager.getInstance(myProject).commitAllDocuments();
431 int offset = myEditor.getCaretModel().getOffset();
432 PsiElement element = myFile.findElementAt(offset);
433 if (element == null) return null;
434 if (element.getParent() instanceof PsiAnonymousClass){
435 try{
436 CodeStyleManager.getInstance(myProject).reformat(element.getParent());
438 catch(IncorrectOperationException e){
439 LOG.error(e);
441 offset = element.getParent().getTextRange().getEndOffset() - 1;
442 myEditor.getCaretModel().moveToOffset(offset);
443 myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
444 myEditor.getSelectionModel().removeSelection();
446 final SmartPsiElementPointer<PsiElement> pointer = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(element);
447 return new Runnable() {
448 public void run(){
449 CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
450 public void run() {
451 PsiDocumentManager.getInstance(myProject).commitDocument(myDocument);
452 PsiElement element = pointer.getElement();
453 if (element == null) return;
455 while(true){
456 if (element instanceof PsiFile) return;
457 PsiElement parent = element.getParent();
458 if (parent instanceof PsiAnonymousClass) break;
459 element = parent;
461 final PsiAnonymousClass aClass = (PsiAnonymousClass)element.getParent();
463 final Collection<CandidateInfo> candidatesToImplement = OverrideImplementUtil.getMethodsToOverrideImplement(aClass, true);
464 boolean invokeOverride = candidatesToImplement.isEmpty();
465 if (invokeOverride){
466 chooseAndOverrideMethodsInAdapter(myProject, myEditor, aClass);
468 else{
469 ApplicationManager.getApplication().runWriteAction(new Runnable() {
470 public void run() {
471 try{
472 List<PsiMethod> methods = OverrideImplementUtil.overrideOrImplementMethodCandidates(aClass, candidatesToImplement, false);
473 List<PsiGenerationInfo<PsiMethod>> prototypes = OverrideImplementUtil.convert2GenerationInfos(methods);
474 List<PsiGenerationInfo<PsiMethod>> resultMembers = GenerateMembersUtil.insertMembersBeforeAnchor(aClass, null, prototypes);
475 GenerateMembersUtil.positionCaret(myEditor, resultMembers.get(0).getPsiMember(), true);
477 catch(IncorrectOperationException ioe){
478 LOG.error(ioe);
484 clear();
486 }, CompletionBundle.message("completion.smart.type.generate.anonymous.body"), null, UndoConfirmationPolicy.DEFAULT, myDocument);
491 private static void chooseAndOverrideMethodsInAdapter(final Project project, final Editor editor, final PsiAnonymousClass aClass) {
492 PsiClass baseClass = aClass.getBaseClassType().resolve();
493 if (baseClass == null) return;
494 PsiMethod[] allBaseMethods = baseClass.getMethods();
495 if(allBaseMethods.length == 0) return;
497 List<PsiMethodMember> methods = new ArrayList<PsiMethodMember>();
498 for (final PsiMethod method : allBaseMethods) {
499 if (OverrideImplementUtil.isOverridable(method)) {
500 methods.add(new PsiMethodMember(method, PsiSubstitutor.UNKNOWN));
504 boolean canInsertOverride = PsiUtil.isLanguageLevel5OrHigher(aClass) && (PsiUtil.isLanguageLevel6OrHigher(aClass) || !aClass.isInterface());
505 final PsiMethodMember[] array = methods.toArray(new PsiMethodMember[methods.size()]);
506 final MemberChooser<PsiMethodMember> chooser = new MemberChooser<PsiMethodMember>(array, false, true, project, canInsertOverride);
507 chooser.setTitle(CompletionBundle.message("completion.smarttype.select.methods.to.override"));
508 chooser.setCopyJavadocVisible(true);
510 chooser.show();
511 List<PsiMethodMember> selected = chooser.getSelectedElements();
512 if (selected == null || selected.isEmpty()) return;
515 try{
516 final List<PsiGenerationInfo<PsiMethod>> prototypes = OverrideImplementUtil.overrideOrImplementMethods(aClass, selected, chooser.isCopyJavadoc(), chooser.isInsertOverrideAnnotation());
518 final int offset = editor.getCaretModel().getOffset();
520 ApplicationManager.getApplication().runWriteAction(new Runnable() {
521 public void run() {
522 try{
523 for (PsiGenerationInfo<PsiMethod> prototype : prototypes) {
524 PsiStatement[] statements = prototype.getPsiMember().getBody().getStatements();
525 if (statements.length > 0 && PsiType.VOID.equals(prototype.getPsiMember().getReturnType())) {
526 statements[0].delete(); // remove "super(..)" call
530 List<PsiGenerationInfo<PsiMethod>> resultMembers = GenerateMembersUtil.insertMembersAtOffset(aClass.getContainingFile(), offset, prototypes);
531 GenerateMembersUtil.positionCaret(editor, resultMembers.get(0).getPsiMember(), true);
533 catch(IncorrectOperationException e){
534 LOG.error(e);
539 catch(IncorrectOperationException ioe){
540 LOG.error(ioe);
544 @Override
545 protected void populateInsertMap(@NotNull final PsiFile file, @NotNull final OffsetMap offsetMap) {
546 JavaCompletionUtil.initOffsets(file, file.getProject(), offsetMap);
549 public static void addImportForItem(PsiFile file, int startOffset, LookupElement item) throws IncorrectOperationException {
550 PsiDocumentManager.getInstance(file.getProject()).commitAllDocuments();
552 Object o = item.getObject();
553 if (o instanceof PsiClass){
554 PsiClass aClass = (PsiClass)o;
555 if (aClass.getQualifiedName() == null) return;
556 final String lookupString = item.getLookupString();
557 int length = lookupString.length();
558 final int i = lookupString.indexOf('<');
559 if (i >= 0) length = i;
560 final int newOffset = addImportForClass(file, startOffset, startOffset + length, aClass);
561 shortenReference(file, newOffset);
563 else if (o instanceof PsiType){
564 PsiType type = ((PsiType)o).getDeepComponentType();
565 if (type instanceof PsiClassType) {
566 PsiClass refClass = ((PsiClassType) type).resolve();
567 if (refClass != null){
568 int length = refClass.getName().length();
569 addImportForClass(file, startOffset, startOffset + length, refClass);
573 else if (o instanceof PsiMethod){
574 PsiMethod method = (PsiMethod)o;
575 if (method.isConstructor()){
576 PsiClass aClass = method.getContainingClass();
577 if (aClass != null){
578 int length = method.getName().length();
579 addImportForClass(file, startOffset, startOffset + length, aClass);
585 //need to shorten references in type argument list
586 private static void shortenReference(final PsiFile file, final int offset) throws IncorrectOperationException {
587 final PsiDocumentManager manager = PsiDocumentManager.getInstance(file.getProject());
588 final Document document = manager.getDocument(file);
589 manager.commitDocument(document);
590 final PsiReference ref = file.findReferenceAt(offset);
591 if (ref instanceof PsiJavaCodeReferenceElement) {
592 JavaCodeStyleManager.getInstance(file.getProject()).shortenClassReferences((PsiJavaCodeReferenceElement)ref);
596 private static int addImportForClass(PsiFile file, int startOffset, int endOffset, PsiClass aClass) throws IncorrectOperationException {
597 if (!aClass.isValid()) {
598 return startOffset;
601 SmartPsiElementPointer<PsiClass> pointer = SmartPointerManager.getInstance(file.getProject()).createSmartPsiElementPointer(aClass);
602 LOG.assertTrue(CommandProcessor.getInstance().getCurrentCommand() != null);
603 LOG.assertTrue(ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().getCurrentWriteAction(null) != null);
605 final PsiManager manager = file.getManager();
607 final Document document = FileDocumentManager.getInstance().getDocument(file.getViewProvider().getVirtualFile());
609 final PsiReference reference = file.findReferenceAt(startOffset);
610 if (reference != null) {
611 final PsiElement resolved = reference.resolve();
612 if (resolved instanceof PsiClass) {
613 if (((PsiClass)resolved).getQualifiedName() == null || manager.areElementsEquivalent(aClass, resolved)) {
614 return startOffset;
619 String name = aClass.getName();
620 document.replaceString(startOffset, endOffset, name);
621 //PsiDocumentManager.getInstance(manager.getProject()).commitAllDocuments();
623 final RangeMarker toDelete = insertSpace(endOffset, document);
625 PsiDocumentManager.getInstance(manager.getProject()).commitAllDocuments();
627 int newStartOffset = startOffset;
628 PsiElement element = file.findElementAt(startOffset);
629 if (element instanceof PsiIdentifier) {
630 PsiElement parent = element.getParent();
631 if (parent instanceof PsiJavaCodeReferenceElement && !((PsiJavaCodeReferenceElement)parent).isQualified() && !(parent.getParent() instanceof PsiPackageStatement)) {
632 PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement)parent;
634 if (!aClass.getManager().areElementsEquivalent(aClass, resolveReference(ref))) {
635 final PsiElement pointerElement = pointer.getElement();
636 if (pointerElement instanceof PsiClass) {
637 PsiElement newElement;
638 if (!(ref instanceof PsiImportStaticReferenceElement)) {
639 newElement = ref.bindToElement(pointerElement);
641 else {
642 newElement = ((PsiImportStaticReferenceElement)ref).bindToTargetClass((PsiClass)pointerElement);
644 RangeMarker marker = document.createRangeMarker(newElement.getTextRange());
645 CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(newElement);
646 newStartOffset = marker.getStartOffset();
652 if (toDelete.isValid()) {
653 document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
656 return newStartOffset;
659 public static RangeMarker insertSpace(final int endOffset, final Document document) {
660 final CharSequence chars = document.getCharsSequence();
661 final int length = chars.length();
662 final RangeMarker toDelete;
663 if (endOffset < length && Character.isJavaIdentifierPart(chars.charAt(endOffset))){
664 document.insertString(endOffset, " ");
665 toDelete = document.createRangeMarker(endOffset, endOffset + 1);
666 } else if (endOffset >= length) {
667 toDelete = document.createRangeMarker(length, length);
669 else {
670 toDelete = document.createRangeMarker(endOffset, endOffset);
672 toDelete.setGreedyToLeft(true);
673 toDelete.setGreedyToRight(true);
674 return toDelete;
677 static PsiElement resolveReference(final PsiReference psiReference) {
678 if (psiReference instanceof PsiPolyVariantReference) {
679 final ResolveResult[] results = ((PsiPolyVariantReference)psiReference).multiResolve(true);
680 if (results.length == 1) return results[0].getElement();
682 return psiReference.resolve();
685 public static class InsertHandlerState{
686 int tailOffset;
687 int caretOffset;
689 public InsertHandlerState(int caretOffset, int tailOffset){
690 this.caretOffset = caretOffset;
691 this.tailOffset = tailOffset;