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() {
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());
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
);
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);
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();
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());
154 int offset
= saveMaker
.getStartOffset();
155 myEditor
.getCaretModel().moveToOffset(offset
);
156 myEditor
.getScrollingModel().scrollToCaret(ScrollType
.RELATIVE
);
157 myEditor
.getSelectionModel().removeSelection();
162 if (completionChar
== '#') {
163 context
.setLaterRunnable(new Runnable() {
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() {
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
){
211 private boolean isAtTokenNeeded() {
212 HighlighterIterator iterator
= ((EditorEx
)myContext
.getEditor()).getHighlighter().createIterator(myContext
.getStartOffset());
213 LOG
.assertTrue(iterator
.getTokenType() == JavaTokenType
.IDENTIFIER
);
215 if (iterator
.getTokenType() == TokenType
.WHITE_SPACE
) iterator
.retreat();
216 return iterator
.getTokenType() != JavaTokenType
.AT
&& iterator
.getTokenType() != JavaTokenType
.DOT
;
219 private void handleBrackets(){
221 final Integer bracketsAttr
= (Integer
)myLookupItem
.getUserData(LookupItem
.BRACKETS_COUNT_ATTR
);
222 if (bracketsAttr
!= null){
223 int count
= bracketsAttr
.intValue();
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
;
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;
245 myState
.caretOffset
= myContext
.getOffsetMap().getOffset(JavaCompletionUtil
.LPAREN_OFFSET
) + 1;
248 myState
.caretOffset
= myContext
.getOffsetMap().getOffset(JavaCompletionUtil
.ARG_LIST_END_OFFSET
);
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, ")");
268 myState
.tailOffset
+= 2;
269 myState
.caretOffset
++;
272 if (tailType
!= TailTypes
.CALL_RPARENTH
|| generateAnonymousBody
) {
273 myState
.tailOffset
+= 2;
274 myState
.caretOffset
+= 2;
277 myState
.tailOffset
++;
278 myState
.caretOffset
++;
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
);
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;
313 } else if (insertingAnnotationWithParameters()) {
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) {
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
))
347 else if (insertingAnnotationWithParameters()) {
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;
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
;
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
){
436 CodeStyleManager
.getInstance(myProject
).reformat(element
.getParent());
438 catch(IncorrectOperationException 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() {
449 CommandProcessor
.getInstance().executeCommand(myProject
, new Runnable() {
451 PsiDocumentManager
.getInstance(myProject
).commitDocument(myDocument
);
452 PsiElement element
= pointer
.getElement();
453 if (element
== null) return;
456 if (element
instanceof PsiFile
) return;
457 PsiElement parent
= element
.getParent();
458 if (parent
instanceof PsiAnonymousClass
) break;
461 final PsiAnonymousClass aClass
= (PsiAnonymousClass
)element
.getParent();
463 final Collection
<CandidateInfo
> candidatesToImplement
= OverrideImplementUtil
.getMethodsToOverrideImplement(aClass
, true);
464 boolean invokeOverride
= candidatesToImplement
.isEmpty();
466 chooseAndOverrideMethodsInAdapter(myProject
, myEditor
, aClass
);
469 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
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
){
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);
511 List
<PsiMethodMember
> selected
= chooser
.getSelectedElements();
512 if (selected
== null || selected
.isEmpty()) return;
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() {
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
){
539 catch(IncorrectOperationException ioe
){
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();
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()) {
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
)) {
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
);
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
);
670 toDelete
= document
.createRangeMarker(endOffset
, endOffset
);
672 toDelete
.setGreedyToLeft(true);
673 toDelete
.setGreedyToRight(true);
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
{
689 public InsertHandlerState(int caretOffset
, int tailOffset
){
690 this.caretOffset
= caretOffset
;
691 this.tailOffset
= tailOffset
;