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
.completion
.scope
.CompletionElement
;
19 import com
.intellij
.codeInsight
.completion
.scope
.JavaCompletionProcessor
;
20 import com
.intellij
.codeInsight
.lookup
.*;
21 import com
.intellij
.codeInspection
.InspectionProfile
;
22 import com
.intellij
.codeInspection
.InspectionProfileEntry
;
23 import com
.intellij
.codeInspection
.SuppressionUtil
;
24 import com
.intellij
.codeInspection
.ex
.LocalInspectionToolWrapper
;
25 import com
.intellij
.codeInspection
.javaDoc
.JavaDocLocalInspection
;
26 import com
.intellij
.openapi
.diagnostic
.Logger
;
27 import com
.intellij
.openapi
.editor
.CaretModel
;
28 import com
.intellij
.openapi
.editor
.Editor
;
29 import com
.intellij
.openapi
.editor
.EditorModificationUtil
;
30 import com
.intellij
.openapi
.editor
.ScrollType
;
31 import com
.intellij
.openapi
.project
.Project
;
32 import com
.intellij
.patterns
.PsiJavaPatterns
;
33 import com
.intellij
.profile
.codeInspection
.InspectionProjectProfileManager
;
34 import com
.intellij
.psi
.*;
35 import com
.intellij
.psi
.codeStyle
.CodeStyleSettings
;
36 import com
.intellij
.psi
.codeStyle
.CodeStyleSettingsManager
;
37 import com
.intellij
.psi
.codeStyle
.JavaCodeStyleManager
;
38 import com
.intellij
.psi
.filters
.TrueFilter
;
39 import com
.intellij
.psi
.impl
.JavaConstantExpressionEvaluator
;
40 import com
.intellij
.psi
.javadoc
.*;
41 import com
.intellij
.psi
.util
.PsiTreeUtil
;
42 import com
.intellij
.psi
.util
.TypeConversionUtil
;
43 import com
.intellij
.util
.IncorrectOperationException
;
44 import com
.intellij
.util
.ProcessingContext
;
45 import com
.intellij
.util
.text
.CharArrayUtil
;
46 import org
.jetbrains
.annotations
.NonNls
;
47 import org
.jetbrains
.annotations
.NotNull
;
49 import java
.util
.ArrayList
;
50 import java
.util
.List
;
51 import java
.util
.StringTokenizer
;
54 * Created by IntelliJ IDEA.
58 * To change this template use Options | File Templates.
60 public class JavaDocCompletionContributor
extends CompletionContributor
{
61 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.completion.JavaDocCompletionContributor");
62 private static final @NonNls String VALUE_TAG
= "value";
63 private static final @NonNls String LINK_TAG
= "link";
65 public JavaDocCompletionContributor() {
66 extend(CompletionType
.BASIC
, PsiJavaPatterns
.psiElement(PsiDocToken
.DOC_TAG_NAME
), new TagChooser());
68 extend(CompletionType
.BASIC
, PsiJavaPatterns
.psiElement().inside(PsiDocTagValue
.class), new CompletionProvider
<CompletionParameters
>(
70 protected void addCompletions(@NotNull final CompletionParameters parameters
, final ProcessingContext context
, @NotNull final CompletionResultSet result
) {
71 final PsiElement position
= parameters
.getPosition();
72 boolean isArg
= PsiJavaPatterns
.psiElement().afterLeaf("(").accepts(position
);
73 PsiDocTag tag
= PsiTreeUtil
.getParentOfType(position
, PsiDocTag
.class);
74 boolean onlyConstants
= !isArg
&& tag
!= null && tag
.getName().equals(VALUE_TAG
);
76 final PsiReference ref
= position
.getContainingFile().findReferenceAt(parameters
.getOffset());
77 if (ref
instanceof PsiJavaReference
) {
80 final JavaCompletionProcessor processor
= new JavaCompletionProcessor(position
, TrueFilter
.INSTANCE
, false, null);
81 ((PsiJavaReference
) ref
).processVariants(processor
);
83 for (final CompletionElement _item
: processor
.getResults()) {
84 LookupItem item
= (LookupItem
)LookupItemUtil
.objectToLookupItem(_item
.getElement());
86 Object o
= item
.getObject();
87 if (!(o
instanceof PsiField
)) continue;
88 PsiField field
= (PsiField
) o
;
89 if (!(field
.hasModifierProperty(PsiModifier
.STATIC
) && field
.getInitializer() != null &&
90 JavaConstantExpressionEvaluator
.computeConstantExpression(field
.getInitializer(), false) != null)) continue;
93 item
.putUserData(LookupItem
.FORCE_SHOW_SIGNATURE_ATTR
, Boolean
.TRUE
);
95 item
.setAutoCompletionPolicy(AutoCompletionPolicy
.NEVER_AUTOCOMPLETE
);
97 item
.setInsertHandler(new MethodSignatureInsertHandler());
98 result
.addElement(item
);
106 public void fillCompletionVariants(final CompletionParameters parameters
, final CompletionResultSet result
) {
107 if (PsiJavaPatterns
.psiElement(PsiDocToken
.DOC_COMMENT_DATA
).accepts(parameters
.getPosition())) return;
109 super.fillCompletionVariants(parameters
, result
);
112 private static class TagChooser
extends CompletionProvider
<CompletionParameters
> {
114 protected void addCompletions(@NotNull final CompletionParameters parameters
, final ProcessingContext context
, @NotNull final CompletionResultSet result
) {
115 List
<String
> ret
= new ArrayList
<String
>();
116 final PsiElement position
= parameters
.getPosition();
117 final PsiDocComment comment
= PsiTreeUtil
.getParentOfType(position
, PsiDocComment
.class);
118 final PsiElement parent
= comment
.getContext();
119 final boolean isInline
= position
.getContext() instanceof PsiInlineDocTag
;
121 final JavadocManager manager
= JavaPsiFacade
.getInstance(position
.getProject()).getJavadocManager();
122 final JavadocTagInfo
[] infos
= manager
.getTagInfos(parent
);
123 for (JavadocTagInfo info
: infos
) {
124 if (info
.getName().equals(SuppressionUtil
.SUPPRESS_INSPECTIONS_TAG_NAME
)) continue;
125 if (isInline
!= (info
.isInline())) continue;
126 ret
.add(info
.getName());
129 InspectionProfile inspectionProfile
=
130 InspectionProjectProfileManager
.getInstance(position
.getProject()).getInspectionProfile();
131 final InspectionProfileEntry inspectionTool
= inspectionProfile
.getInspectionTool(JavaDocLocalInspection
.SHORT_NAME
, position
);
132 JavaDocLocalInspection inspection
= (JavaDocLocalInspection
)((LocalInspectionToolWrapper
)inspectionTool
).getTool();
133 final StringTokenizer tokenizer
= new StringTokenizer(inspection
.myAdditionalJavadocTags
, ", ");
134 while (tokenizer
.hasMoreTokens()) {
135 ret
.add(tokenizer
.nextToken());
137 for (final String s
: ret
) {
138 final LookupItem item
= (LookupItem
)LookupItemUtil
.objectToLookupItem(s
);
140 item
.setInsertHandler(new InlineInsertHandler());
142 result
.addElement(item
);
146 @SuppressWarnings({"HardCodedStringLiteral"})
147 public String
toString() {
148 return "javadoc-tag-chooser";
152 private static class InlineInsertHandler
implements InsertHandler
<LookupItem
> {
153 public void handleInsert(InsertionContext context
, LookupItem item
) {
154 if (context
.getCompletionChar() == Lookup
.REPLACE_SELECT_CHAR
) {
155 final Project project
= context
.getProject();
156 PsiDocumentManager
.getInstance(project
).commitAllDocuments();
157 final Editor editor
= context
.getEditor();
158 final CaretModel caretModel
= editor
.getCaretModel();
159 final int offset
= caretModel
.getOffset();
160 final PsiElement element
= context
.getFile().findElementAt(offset
- 1);
161 PsiDocTag tag
= PsiTreeUtil
.getParentOfType(element
, PsiDocTag
.class);
163 for (PsiElement child
= tag
.getFirstChild(); child
!= null; child
= child
.getNextSibling()) {
164 if (child
instanceof PsiDocToken
) {
165 PsiDocToken token
= (PsiDocToken
)child
;
166 if (token
.getTokenType() == JavaDocTokenType
.DOC_INLINE_TAG_END
) return;
170 final String name
= tag
.getName();
172 final CharSequence chars
= editor
.getDocument().getCharsSequence();
173 final int currentOffset
= caretModel
.getOffset();
174 if (chars
.charAt(currentOffset
) == '}') {
175 caretModel
.moveToOffset(offset
+ 1);
177 else if (chars
.charAt(currentOffset
+ 1) == '}' && chars
.charAt(currentOffset
) == ' ') {
178 caretModel
.moveToOffset(offset
+ 2);
180 else if (name
.equals(LINK_TAG
)) {
181 EditorModificationUtil
.insertStringAtCaret(editor
, " }");
182 caretModel
.moveToOffset(offset
+ 1);
183 editor
.getScrollingModel().scrollToCaret(ScrollType
.RELATIVE
);
184 editor
.getSelectionModel().removeSelection();
187 EditorModificationUtil
.insertStringAtCaret(editor
, "}");
188 caretModel
.moveToOffset(offset
+ 1);
194 private static class MethodSignatureInsertHandler
implements InsertHandler
<LookupItem
> {
195 public void handleInsert(InsertionContext context
, LookupItem item
) {
196 if (!(item
.getObject() instanceof PsiMethod
)) {
199 PsiDocumentManager
.getInstance(context
.getProject()).commitDocument(context
.getEditor().getDocument());
200 final Editor editor
= context
.getEditor();
201 final PsiMethod method
= (PsiMethod
)((LookupItem
)item
).getObject();
203 final PsiParameter
[] parameters
= method
.getParameterList().getParameters();
204 final StringBuffer buffer
= new StringBuffer();
206 final CharSequence chars
= editor
.getDocument().getCharsSequence();
207 int endOffset
= editor
.getCaretModel().getOffset();
208 final Project project
= context
.getProject();
209 int afterSharp
= CharArrayUtil
.shiftBackwardUntil(chars
, endOffset
- 1, "#") + 1;
210 int signatureOffset
= afterSharp
;
212 PsiElement element
= context
.getFile().findElementAt(signatureOffset
- 1);
213 final CodeStyleSettings styleSettings
= CodeStyleSettingsManager
.getSettings(element
.getProject());
214 PsiDocTag tag
= PsiTreeUtil
.getParentOfType(element
, PsiDocTag
.class);
215 if (context
.getCompletionChar() == Lookup
.REPLACE_SELECT_CHAR
) {
216 final PsiDocTagValue valueElement
= tag
.getValueElement();
217 endOffset
= valueElement
.getTextRange().getEndOffset();
219 editor
.getDocument().deleteString(afterSharp
, endOffset
);
220 editor
.getCaretModel().moveToOffset(signatureOffset
);
221 editor
.getScrollingModel().scrollToCaret(ScrollType
.RELATIVE
);
222 editor
.getSelectionModel().removeSelection();
223 buffer
.append(method
.getName() + "(");
224 final int afterParenth
= afterSharp
+ buffer
.length();
225 for (int i
= 0; i
< parameters
.length
; i
++) {
226 final PsiType type
= TypeConversionUtil
.erasure(parameters
[i
].getType());
227 buffer
.append(type
.getCanonicalText());
229 if (i
< parameters
.length
- 1) {
231 if (styleSettings
.SPACE_AFTER_COMMA
) buffer
.append(" ");
235 if (!(tag
instanceof PsiInlineDocTag
)) {
239 final int currentOffset
= editor
.getCaretModel().getOffset();
240 if (chars
.charAt(currentOffset
) == '}') {
247 String insertString
= buffer
.toString();
248 EditorModificationUtil
.insertStringAtCaret(editor
, insertString
);
249 editor
.getCaretModel().moveToOffset(afterSharp
+ buffer
.length());
250 editor
.getScrollingModel().scrollToCaret(ScrollType
.RELATIVE
);
251 PsiDocumentManager
.getInstance(project
).commitDocument(editor
.getDocument());
253 shortenReferences(project
, editor
, context
, afterParenth
);
256 private static void shortenReferences(final Project project
, final Editor editor
, InsertionContext context
, int offset
) {
257 PsiDocumentManager
.getInstance(project
).commitDocument(editor
.getDocument());
258 final PsiElement element
= context
.getFile().findElementAt(offset
);
259 final PsiDocTagValue tagValue
= PsiTreeUtil
.getParentOfType(element
, PsiDocTagValue
.class);
260 if (tagValue
!= null) {
262 JavaCodeStyleManager
.getInstance(project
).shortenClassReferences(tagValue
);
264 catch (IncorrectOperationException e
) {
268 PsiDocumentManager
.getInstance(context
.getProject()).commitAllDocuments();