1 package com
.intellij
.codeInsight
.completion
;
3 import com
.intellij
.codeInsight
.TailType
;
4 import com
.intellij
.codeInsight
.lookup
.*;
5 import com
.intellij
.codeInsight
.template
.Template
;
6 import com
.intellij
.openapi
.application
.ApplicationManager
;
7 import com
.intellij
.openapi
.diagnostic
.Logger
;
8 import com
.intellij
.openapi
.util
.Computable
;
9 import com
.intellij
.openapi
.util
.Condition
;
10 import com
.intellij
.patterns
.ElementPattern
;
11 import static com
.intellij
.patterns
.StandardPatterns
.character
;
12 import static com
.intellij
.patterns
.StandardPatterns
.not
;
13 import com
.intellij
.psi
.*;
14 import com
.intellij
.psi
.filters
.ContextGetter
;
15 import com
.intellij
.psi
.filters
.ElementFilter
;
16 import com
.intellij
.psi
.filters
.TrueFilter
;
17 import com
.intellij
.psi
.impl
.source
.resolve
.reference
.impl
.PsiMultiReference
;
18 import com
.intellij
.psi
.meta
.PsiMetaData
;
19 import com
.intellij
.psi
.util
.PsiUtilBase
;
20 import com
.intellij
.util
.ReflectionCache
;
21 import com
.intellij
.util
.containers
.ContainerUtil
;
22 import org
.jetbrains
.annotations
.NotNull
;
23 import org
.jetbrains
.annotations
.Nullable
;
28 * @deprecated see {@link com.intellij.codeInsight.completion.CompletionContributor}
30 public class CompletionData
{
31 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.completion.CompletionData");
32 private final Set
<Class
> myFinalScopes
= new HashSet
<Class
>();
33 private final List
<CompletionVariant
> myCompletionVariants
= new ArrayList
<CompletionVariant
>();
35 protected CompletionData(){ }
37 protected final void declareFinalScope(Class scopeClass
){
38 myFinalScopes
.add(scopeClass
);
41 protected boolean isScopeFinal(Class scopeClass
){
42 if(myFinalScopes
.contains(scopeClass
))
45 for (final Class myFinalScope
: myFinalScopes
) {
46 if (ReflectionCache
.isAssignable(myFinalScope
, scopeClass
)) {
53 private boolean isScopeAcceptable(PsiElement scope
){
55 for (final CompletionVariant variant
: myCompletionVariants
) {
56 if (variant
.isScopeAcceptable(scope
)) {
63 protected void defineScopeEquivalence(Class scopeClass
, Class equivClass
){
64 final Iterator
<CompletionVariant
> iter
= myCompletionVariants
.iterator();
65 if(isScopeFinal(scopeClass
)){
66 declareFinalScope(equivClass
);
69 while(iter
.hasNext()){
70 final CompletionVariant variant
= iter
.next();
71 if(variant
.isScopeClassAcceptable(scopeClass
)){
72 variant
.includeScopeClass(equivClass
, variant
.isScopeClassFinal(scopeClass
));
79 * @see com.intellij.codeInsight.completion.CompletionContributor
81 protected void registerVariant(CompletionVariant variant
){
82 myCompletionVariants
.add(variant
);
85 public void completeReference(final PsiReference reference
, final Set
<LookupElement
> set
, @NotNull final PsiElement position
, final PsiFile file
,
87 final CompletionVariant
[] variants
= findVariants(position
, file
);
88 ApplicationManager
.getApplication().runReadAction(new Runnable() {
90 boolean hasApplicableVariants
= false;
91 for (CompletionVariant variant
: variants
) {
92 if (variant
.hasReferenceFilter()) {
93 variant
.addReferenceCompletions(reference
, position
, set
, file
, CompletionData
.this);
94 hasApplicableVariants
= true;
98 if (!hasApplicableVariants
) {
99 myGenericVariant
.addReferenceCompletions(reference
, position
, set
, file
, CompletionData
.this);
105 public void addKeywordVariants(Set
<CompletionVariant
> set
, PsiElement position
, final PsiFile file
){
106 set
.addAll(Arrays
.asList(findVariants(position
, file
)));
109 public void completeKeywordsBySet(final Set
<LookupElement
> set
, Set
<CompletionVariant
> variants
, final PsiElement position
,
110 final PrefixMatcher matcher
,
112 for (final CompletionVariant variant
: variants
) {
113 ApplicationManager
.getApplication().runReadAction(new Runnable() {
115 variant
.addKeywords(set
, position
, matcher
, file
, CompletionData
.this);
121 public String
findPrefix(PsiElement insertedElement
, int offsetInFile
){
122 return findPrefixStatic(insertedElement
, offsetInFile
);
125 public CompletionVariant
[] findVariants(final PsiElement position
, final PsiFile file
){
126 return ApplicationManager
.getApplication().runReadAction(new Computable
<CompletionVariant
[]>() {
127 public CompletionVariant
[] compute() {
128 final List
<CompletionVariant
> variants
= new ArrayList
<CompletionVariant
>();
129 PsiElement scope
= position
;
133 while (scope
!= null) {
134 boolean breakFlag
= false;
135 if (isScopeAcceptable(scope
)){
137 for (final CompletionVariant variant
: myCompletionVariants
) {
138 if (variant
.isVariantApplicable(position
, scope
) && !variants
.contains(variant
)) {
139 variants
.add(variant
);
140 if (variant
.isScopeFinal(scope
)) {
146 if(breakFlag
|| isScopeFinal(scope
.getClass()))
148 scope
= scope
.getContext();
149 if (scope
instanceof PsiDirectory
) break;
151 return variants
.toArray(new CompletionVariant
[variants
.size()]);
156 protected final CompletionVariant myGenericVariant
= new CompletionVariant() {
157 public void addReferenceCompletions(PsiReference reference
, PsiElement position
, Set
<LookupElement
> set
, final PsiFile file
,
158 final CompletionData completionData
) {
159 completeReference(reference
, position
, set
, TailType
.NONE
, file
, TrueFilter
.INSTANCE
, this);
164 public static String
getReferencePrefix(@NotNull PsiElement insertedElement
, int offsetInFile
) {
165 final PsiReference ref
= insertedElement
.getContainingFile().findReferenceAt(offsetInFile
);
167 final PsiElement element
= ref
.getElement();
168 final int endIndex
= offsetInFile
- element
.getTextRange().getStartOffset();
169 final int beginIndex
= ref
.getRangeInElement().getStartOffset();
170 if (beginIndex
> endIndex
) {
171 LOG
.error("Inconsistent reference (found at offset not included in its range): ref=" + ref
+ " element=" + element
+ " text=" + element
.getText());
173 if (beginIndex
< 0) {
174 LOG
.error("Inconsistent reference (begin < 0): ref=" + ref
+ " element=" + element
+ "; begin=" + beginIndex
+ " text=" + element
.getText());
176 LOG
.assertTrue(endIndex
>= 0);
177 return element
.getText().substring(beginIndex
, endIndex
);
182 public static String
findPrefixStatic(final PsiElement insertedElement
, final int offsetInFile
) {
183 if(insertedElement
== null) return "";
185 final String prefix
= ApplicationManager
.getApplication().runReadAction(new Computable
<String
>() {
186 public String
compute() {
187 return getReferencePrefix(insertedElement
, offsetInFile
);
190 if (prefix
!= null) return prefix
;
192 if (insertedElement
instanceof PsiPlainText
|| insertedElement
instanceof PsiComment
) {
193 return CompletionUtil
.findJavaIdentifierPrefix(insertedElement
, offsetInFile
);
196 return findPrefixDefault(insertedElement
, offsetInFile
, not(character().javaIdentifierPart()));
199 protected static String
findPrefixDefault(final PsiElement insertedElement
, final int offset
, @NotNull final ElementPattern trimStart
) {
200 String substr
= insertedElement
.getText().substring(0, offset
- insertedElement
.getTextRange().getStartOffset());
201 if (substr
.length() == 0 || Character
.isWhitespace(substr
.charAt(substr
.length() - 1))) return "";
203 substr
= substr
.trim();
206 while (substr
.length() > i
&& trimStart
.accepts(substr
.charAt(i
))) i
++;
207 return substr
.substring(i
).trim();
210 public static LookupItem
objectToLookupItem(Object object
) {
211 if (object
instanceof LookupItem
) return (LookupItem
)object
;
214 TailType tailType
= TailType
.NONE
;
215 if (object
instanceof PsiElement
){
216 s
= PsiUtilBase
.getName((PsiElement
) object
);
218 else if (object
instanceof PsiMetaData
) {
219 s
= ((PsiMetaData
)object
).getName();
221 else if (object
instanceof String
) {
224 else if (object
instanceof Template
) {
225 s
= ((Template
) object
).getKey();
227 else if (object
instanceof PresentableLookupValue
) {
228 s
= ((PresentableLookupValue
)object
).getPresentation();
231 LOG
.assertTrue(false, "Null string for object: " + object
+ " of class " + (object
!= null ?object
.getClass():null));
234 LookupItem item
= new LookupItem(object
, s
);
236 if (object
instanceof LookupValueWithUIHint
&& ((LookupValueWithUIHint
) object
).isBold()) {
239 if (object
instanceof LookupValueWithTail
) {
240 item
.setAttribute(LookupItem
.TAIL_TEXT_ATTR
, " " + ((LookupValueWithTail
)object
).getTailText());
242 item
.setAttribute(CompletionUtil
.TAIL_TYPE_ATTR
, tailType
);
247 protected void addLookupItem(Set
<LookupElement
> set
, TailType tailType
, @NotNull Object completion
, final PsiFile file
,
248 final CompletionVariant variant
) {
249 if (completion
instanceof LookupElement
&& !(completion
instanceof LookupItem
)) {
250 set
.add((LookupElement
)completion
);
254 LookupItem ret
= objectToLookupItem(completion
);
255 if(ret
== null) return;
257 final InsertHandler insertHandler
= variant
.getInsertHandler();
258 if(insertHandler
!= null && ret
.getInsertHandler() == null) {
259 ret
.setInsertHandler(insertHandler
);
260 ret
.setTailType(TailType
.UNKNOWN
);
262 else if (tailType
!= TailType
.NONE
) {
263 ret
.setTailType(tailType
);
265 final Map
<Object
, Object
> itemProperties
= variant
.getItemProperties();
266 for (final Object key
: itemProperties
.keySet()) {
267 ret
.setAttribute(key
, itemProperties
.get(key
));
273 protected void completeReference(final PsiReference reference
, final PsiElement position
, final Set
<LookupElement
> set
, final TailType tailType
,
275 final ElementFilter filter
,
276 final CompletionVariant variant
) {
277 if (reference
instanceof PsiMultiReference
) {
278 final PsiReference
[] references
= getReferences((PsiMultiReference
)reference
);
279 for (PsiReference ref
: references
) {
280 completeReference(ref
, position
, set
, tailType
, file
, filter
, variant
);
284 final Object
[] completions
= reference
.getVariants();
285 if(completions
== null) return;
287 for (Object completion
: completions
) {
288 if (completion
== null) {
289 LOG
.assertTrue(false, "Position=" + position
+ "\n;Reference=" + reference
+ "\n;variants=" + Arrays
.toString(completions
));
291 if (completion
instanceof PsiElement
) {
292 final PsiElement psiElement
= (PsiElement
)completion
;
293 if (filter
.isClassAcceptable(psiElement
.getClass()) && filter
.isAcceptable(psiElement
, position
)) {
294 addLookupItem(set
, tailType
, completion
, file
, variant
);
298 if (completion
instanceof LookupItem
) {
299 final Object o
= ((LookupItem
)completion
).getObject();
300 if (o
instanceof PsiElement
) {
301 if (!filter
.isClassAcceptable(o
.getClass()) || !filter
.isAcceptable(o
, position
)) continue;
304 addLookupItem(set
, tailType
, completion
, file
, variant
);
310 protected PsiReference
[] getReferences(final PsiMultiReference multiReference
) {
311 final PsiReference
[] references
= multiReference
.getReferences();
312 final List
<PsiReference
> hard
= ContainerUtil
.findAll(references
, new Condition
<PsiReference
>() {
313 public boolean value(final PsiReference object
) {
314 return !object
.isSoft();
317 if (!hard
.isEmpty()) {
318 return hard
.toArray(new PsiReference
[hard
.size()]);
323 protected void addKeywords(final Set
<LookupElement
> set
, final PsiElement position
, final PrefixMatcher matcher
, final PsiFile file
,
324 final CompletionVariant variant
, final Object comp
, final TailType tailType
) {
325 if (comp
instanceof String
) {
326 addKeyword(set
, tailType
, comp
, matcher
, file
, variant
);
329 final CompletionContext context
= position
.getUserData(CompletionContext
.COMPLETION_CONTEXT_KEY
);
330 if (comp
instanceof ContextGetter
) {
331 final Object
[] elements
= ((ContextGetter
)comp
).get(position
, context
);
332 for (Object element
: elements
) {
333 addLookupItem(set
, tailType
, element
, file
, variant
);
336 // TODO: KeywordChooser -> ContextGetter
337 else if (comp
instanceof KeywordChooser
) {
338 final String
[] keywords
= ((KeywordChooser
)comp
).getKeywords(context
, position
);
339 for (String keyword
: keywords
) {
340 addKeyword(set
, tailType
, keyword
, matcher
, file
, variant
);
346 private void addKeyword(Set
<LookupElement
> set
, final TailType tailType
, final Object comp
, final PrefixMatcher matcher
,
348 final CompletionVariant variant
) {
349 for (final LookupElement item
: set
) {
350 if (item
.getObject().toString().equals(comp
.toString())) {
354 addLookupItem(set
, tailType
, comp
, file
, variant
);