a313f82b2c4e1cc93f71425914a571ca7aae0aff
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / completion / GroovyCompletionData.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package org.jetbrains.plugins.groovy.lang.completion;
18
19
20 import com.intellij.codeInsight.TailType;
21 import com.intellij.codeInsight.completion.CompletionData;
22 import com.intellij.codeInsight.completion.CompletionVariant;
23 import com.intellij.codeInsight.lookup.LookupElement;
24 import com.intellij.codeInsight.lookup.LookupItem;
25 import com.intellij.codeInsight.lookup.LookupElementDecorator;
26 import com.intellij.openapi.diagnostic.Logger;
27 import com.intellij.psi.PsiElement;
28 import com.intellij.psi.PsiFile;
29 import com.intellij.psi.PsiReference;
30 import com.intellij.psi.filters.*;
31 import com.intellij.psi.filters.position.LeftNeighbour;
32 import com.intellij.psi.filters.position.ParentElementFilter;
33 import com.intellij.psi.impl.source.tree.LeafPsiElement;
34 import gnu.trove.THashSet;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.plugins.groovy.lang.completion.filters.classdef.ExtendsFilter;
37 import org.jetbrains.plugins.groovy.lang.completion.filters.classdef.ImplementsFilter;
38 import org.jetbrains.plugins.groovy.lang.completion.filters.control.BranchFilter;
39 import org.jetbrains.plugins.groovy.lang.completion.filters.control.ControlStructureFilter;
40 import org.jetbrains.plugins.groovy.lang.completion.filters.control.additional.CaseDefaultFilter;
41 import org.jetbrains.plugins.groovy.lang.completion.filters.control.additional.CatchFinallyFilter;
42 import org.jetbrains.plugins.groovy.lang.completion.filters.control.additional.ElseFilter;
43 import org.jetbrains.plugins.groovy.lang.completion.filters.exprs.InstanceOfFilter;
44 import org.jetbrains.plugins.groovy.lang.completion.filters.exprs.SimpleExpressionFilter;
45 import org.jetbrains.plugins.groovy.lang.completion.filters.modifiers.*;
46 import org.jetbrains.plugins.groovy.lang.completion.filters.toplevel.AnnotationFilter;
47 import org.jetbrains.plugins.groovy.lang.completion.filters.toplevel.ClassInterfaceEnumFilter;
48 import org.jetbrains.plugins.groovy.lang.completion.filters.toplevel.ImportFilter;
49 import org.jetbrains.plugins.groovy.lang.completion.filters.toplevel.PackageFilter;
50 import org.jetbrains.plugins.groovy.lang.completion.filters.types.BuiltInTypeAsArgumentFilter;
51 import org.jetbrains.plugins.groovy.lang.completion.filters.types.BuiltInTypeFilter;
52 import org.jetbrains.plugins.groovy.lang.completion.getters.SuggestedVariableNamesGetter;
53 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
54
55 import java.util.Set;
56
57 /**
58  * @author ilyas
59  */
60 public class GroovyCompletionData extends CompletionData {
61   private final static Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.lang.completion.GroovyCompletionData");
62   public static final String[] BUILT_IN_TYPES = {"boolean", "byte", "char", "short", "int", "float", "long", "double", "void"};
63   public static final String[] MODIFIERS = new String[]{"private", "public", "protected", "transient", "abstract", "native", "volatile", "strictfp"};
64
65   public GroovyCompletionData() {
66     registerAllCompletions();
67   }
68
69   /**
70    * Registers completions on top level of Groovy script file
71    */
72   private void registerAllCompletions() {
73     registerPackageCompletion();
74     registerImportCompletion();
75
76     registerClassInterfaceEnumAnnotationCompletion();
77     registerControlCompletion();
78     registerSimpleExprsCompletion();
79     registerBuiltInTypeCompletion();
80     registerBuiltInTypesAsArgumentCompletion();
81     registerInstanceofCompletion();
82     registerThrowsCompletion();
83     registerBranchCompletion();
84     registerModifierCompletion();
85     registerSynchronizedCompletion();
86     registerFinalCompletion();
87
88     registerSuggestVariableNameCompletion();
89   }
90
91   private void registerSuggestVariableNameCompletion() {
92     CompletionVariant variant = new CompletionVariant(new ParentElementFilter(new ClassFilter(GrVariable.class)));
93     variant.includeScopeClass(LeafPsiElement.class);
94     variant.addCompletion(new SuggestedVariableNamesGetter(), TailType.NONE);
95     registerVariant(variant);
96   }
97
98
99   private void registerPackageCompletion() {
100     registerStandardCompletion(new PackageFilter(), "package");
101   }
102
103   private void registerClassInterfaceEnumAnnotationCompletion() {
104     registerStandardCompletion(new ClassInterfaceEnumFilter(), "class", "interface", "enum");
105     registerStandardCompletion(new AnnotationFilter(), "interface");
106     registerStandardCompletion(new ExtendsFilter(), "extends");
107     registerStandardCompletion(new ImplementsFilter(), "implements");
108   }
109
110   private void registerControlCompletion() {
111     String[] controlKeywords = {"try", "while", "with", "switch", "for", "return", "throw", "assert", "synchronized",};
112
113     registerStandardCompletion(new ControlStructureFilter(), controlKeywords);
114     registerStandardCompletion(new CaseDefaultFilter(), "case", "default");
115     registerStandardCompletion(new CatchFinallyFilter(), "catch", "finally");
116     registerStandardCompletion(new ElseFilter(), "else");
117
118
119   }
120
121   private void registerBuiltInTypeCompletion() {
122     registerStandardCompletion(new AndFilter(new BuiltInTypeFilter(), new NotFilter(new ThrowsFilter())), BUILT_IN_TYPES);
123   }
124
125   private void registerBuiltInTypesAsArgumentCompletion() {
126     AndFilter filter = new AndFilter(new BuiltInTypeAsArgumentFilter(), new NotFilter(new ThrowsFilter()));
127     LeftNeighbour afterDotFilter = new LeftNeighbour(new TextFilter("."));
128     CompletionVariant variant = new CompletionVariant(new AndFilter(new NotFilter(afterDotFilter), filter));
129     variant.includeScopeClass(LeafPsiElement.class);
130     for (String completion : BUILT_IN_TYPES) {
131       variant.addCompletion(completion, TailType.SPACE);
132     }
133     registerVariant(variant);
134   }
135
136   private void registerSimpleExprsCompletion() {
137     String[] exprs = {"true", "false", "null", "super", "new", "this"};
138     registerStandardCompletion(new SimpleExpressionFilter(), exprs);
139   }
140
141   private void registerThrowsCompletion() {
142     registerStandardCompletion(new ThrowsFilter(), "throws");
143   }
144
145   private void registerFinalCompletion() {
146     registerStandardCompletion(new AndFilter(new FinalFilter(), new NotFilter(new ThrowsFilter())), "final");
147   }
148
149   private void registerSynchronizedCompletion() {
150     registerStandardCompletion(new SynchronizedFilter(), "synchronized");
151   }
152
153   private void registerImportCompletion() {
154     registerStandardCompletion(new ImportFilter(), "import");
155   }
156
157   private void registerInstanceofCompletion() {
158     registerStandardCompletion(new InstanceOfFilter(), "instanceof");
159   }
160
161   private void registerBranchCompletion() {
162     registerStandardCompletion(new BranchFilter(), "break", "continue");
163   }
164
165   private void registerModifierCompletion() {
166     registerStandardCompletion(new ModifiersFilter(), MODIFIERS);
167     registerStandardCompletion(new LeftNeighbour(new PreviousModifierFilter()), "private", "public", "protected", "transient", "abstract",
168                                "native", "volatile", "strictfp", "synchronized", "static");
169     registerStandardCompletion(new StaticFilter(), "static");
170   }
171
172
173   @Override
174   public void completeReference(final PsiReference reference,
175                                 final Set<LookupElement> set,
176                                 @NotNull final PsiElement position,
177                                 final PsiFile file,
178                                 final int offset) {
179     super.completeReference(reference, set, position, file, offset);
180     Set<LookupElement> result = new THashSet<LookupElement>();
181     for (final LookupElement element : set) {
182       result.add(LookupElementDecorator.withInsertHandler(element, new GroovyInsertHandlerAdapter()));
183     }
184     set.clear();
185     set.addAll(result);
186   }
187
188
189   /**
190    * Template to add all standard keywords completions
191    *
192    * @param filter   - Semantic filter for given keywords
193    * @param keywords - Keywords to be completed
194    */
195   private void registerStandardCompletion(ElementFilter filter, String... keywords) {
196     LeftNeighbour afterDotFilter = new LeftNeighbour(new TextFilter("."));
197     CompletionVariant variant = new CompletionVariant(new AndFilter(new NotFilter(afterDotFilter), filter));
198     variant.setItemProperty(LookupItem.HIGHLIGHTED_ATTR, "");
199     variant.includeScopeClass(LeafPsiElement.class);
200     variant.setInsertHandler(new GroovyInsertHandlerAdapter());
201     addCompletions(variant, keywords);
202     registerVariant(variant);
203   }
204
205
206   public String findPrefix(PsiElement insertedElement, int offset) {
207     if (insertedElement == null) return "";
208     final String text = insertedElement.getText();
209     final int offsetInElement = offset - insertedElement.getTextRange().getStartOffset();
210     int start = offsetInElement - 1;
211     while (start >= 0) {
212       final char c = text.charAt(start);
213       if (!Character.isJavaIdentifierPart(c) && c != '\'') break;
214       --start;
215     }
216
217     return text.substring(start + 1, offsetInElement).trim();
218   }
219
220   /**
221    * Adds all completion variants in sequence
222    *
223    * @param comps   Given completions
224    * @param variant Variant for completions
225    */
226   private void addCompletions(CompletionVariant variant, String... comps) {
227     for (String completion : comps) {
228       variant.addCompletion(completion, TailType.SPACE);
229     }
230   }
231
232
233 }