typo
[fedora-idea.git] / lang-impl / src / com / intellij / codeInsight / completion / CompletionData.java
blob6199ac4d8b1048c6afa6eff9c963876a2c2ff6b0
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;
25 import java.util.*;
27 /**
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))
43 return true;
45 for (final Class myFinalScope : myFinalScopes) {
46 if (ReflectionCache.isAssignable(myFinalScope, scopeClass)) {
47 return true;
50 return false;
53 private boolean isScopeAcceptable(PsiElement scope){
55 for (final CompletionVariant variant : myCompletionVariants) {
56 if (variant.isScopeAcceptable(scope)) {
57 return true;
60 return false;
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));
77 /**
78 * @deprecated
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,
86 final int offset){
87 final CompletionVariant[] variants = findVariants(position, file);
88 ApplicationManager.getApplication().runReadAction(new Runnable() {
89 public void run() {
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,
111 final PsiFile file){
112 for (final CompletionVariant variant : variants) {
113 ApplicationManager.getApplication().runReadAction(new Runnable() {
114 public void run() {
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;
130 if(scope == null){
131 scope = file;
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)) {
141 breakFlag = true;
146 if(breakFlag || isScopeFinal(scope.getClass()))
147 break;
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);
163 @Nullable
164 public static String getReferencePrefix(@NotNull PsiElement insertedElement, int offsetInFile) {
165 final PsiReference ref = insertedElement.getContainingFile().findReferenceAt(offsetInFile);
166 if(ref != null) {
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);
179 return null;
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();
205 int i = 0;
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;
213 String s = null;
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) {
222 s = (String)object;
224 else if (object instanceof Template) {
225 s = ((Template) object).getKey();
227 else if (object instanceof PresentableLookupValue) {
228 s = ((PresentableLookupValue)object).getPresentation();
230 else {
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()) {
237 item.setBold();
239 if (object instanceof LookupValueWithTail) {
240 item.setAttribute(LookupItem.TAIL_TEXT_ATTR, " " + ((LookupValueWithTail)object).getTailText());
242 item.setAttribute(CompletionUtil.TAIL_TYPE_ATTR, tailType);
243 return item;
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);
251 return;
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));
270 set.add(ret);
273 protected void completeReference(final PsiReference reference, final PsiElement position, final Set<LookupElement> set, final TailType tailType,
274 final PsiFile file,
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);
283 else{
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);
297 else {
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()]);
320 return references;
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);
328 else {
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,
347 final PsiFile file,
348 final CompletionVariant variant) {
349 for (final LookupElement item : set) {
350 if (item.getObject().toString().equals(comp.toString())) {
351 return;
354 addLookupItem(set, tailType, comp, file, variant);