]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/Completions.java
New SCL completion implementation
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / completions / Completions.java
1 package org.simantics.scl.compiler.completions;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.concurrent.atomic.AtomicInteger;
7
8 import org.simantics.scl.compiler.common.names.Name;
9 import org.simantics.scl.compiler.compilation.CompilationContext;
10 import org.simantics.scl.compiler.compilation.DeclarationClassification;
11 import org.simantics.scl.compiler.compilation.Elaboration;
12 import org.simantics.scl.compiler.completions.parsing.RobustModuleParser;
13 import org.simantics.scl.compiler.elaboration.expressions.EVar;
14 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
15 import org.simantics.scl.compiler.environment.Environment;
16 import org.simantics.scl.compiler.environment.EnvironmentFactory;
17 import org.simantics.scl.compiler.environment.EnvironmentFactoryImpl;
18 import org.simantics.scl.compiler.environment.Namespace;
19 import org.simantics.scl.compiler.environment.filter.AcceptAllNamespaceFilter;
20 import org.simantics.scl.compiler.internal.parsing.declarations.ConstructorAst;
21 import org.simantics.scl.compiler.internal.parsing.declarations.DDataAst;
22 import org.simantics.scl.compiler.internal.parsing.declarations.DDocumentationAst;
23 import org.simantics.scl.compiler.internal.parsing.declarations.DTypeAst;
24 import org.simantics.scl.compiler.internal.parsing.declarations.DValueTypeAst;
25 import org.simantics.scl.compiler.internal.parsing.types.TypeAst;
26 import org.simantics.scl.compiler.module.ImportDeclaration;
27 import org.simantics.scl.compiler.types.TCon;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import gnu.trove.procedure.TObjectProcedure;
32 import gnu.trove.set.hash.THashSet;
33
34 public class Completions {
35     private static final Logger LOGGER = LoggerFactory.getLogger(Completions.class);
36     
37     public static List<Completion> findCompletions(CompletionRequest request) {
38         //LOGGER.debug("findCompletions");
39         AtomicInteger maxNumberOfCompletions = new AtomicInteger(request.maxNumberOfCompletions);
40         ArrayList<Completion> completions = new ArrayList<Completion>();
41         
42         String moduleName = request.moduleSource.getModuleName();
43         CompilationContext context = new CompilationContext();
44         
45         String prefix = PrefixUtil.findPrefix(request.sourceText, request.position).toLowerCase();
46         //LOGGER.debug("    prefix = {}", prefix);
47         
48         DeclarationClassification declarations = RobustModuleParser.parse(context, request.sourceText);
49
50         
51         // Find local completions
52         localDefs: if(!prefix.contains(".")) {
53             THashSet<String> valuesAlreadyAdded = new THashSet<String>(); 
54             for(DDataAst dataAst : declarations.dataTypesAst) {
55                 if(dataAst.name.toLowerCase().startsWith(prefix)) {
56                     completions.add(createLocalTypeCompletion(request.position, prefix, declarations, moduleName, dataAst.name));
57                     if(maxNumberOfCompletions.decrementAndGet() <= 0)
58                         break localDefs;
59                 }
60                 for(ConstructorAst constructor : dataAst.constructors) {
61                     if(constructor.name.text.toLowerCase().startsWith(prefix) && valuesAlreadyAdded.add(constructor.name.text)) {
62                         completions.add(createLocalValueCompletion(request.position, prefix, declarations, moduleName, constructor.name.text, null));
63                         if(maxNumberOfCompletions.decrementAndGet() <= 0)
64                             break localDefs;
65                     }
66                 }
67             }
68             for(DTypeAst typeAst : declarations.typeAliasesAst) {
69                 if(typeAst.name.toLowerCase().startsWith(prefix)) {
70                     completions.add(createLocalTypeCompletion(request.position, prefix, declarations, moduleName, typeAst.name));
71                     if(maxNumberOfCompletions.decrementAndGet() <= 0)
72                         break localDefs;
73                 }
74             }
75             
76             for(DValueTypeAst valueType : declarations.typeAnnotationsAst) {
77                 for(EVar var : valueType.names) {
78                     if(var.name.toLowerCase().startsWith(prefix) && valuesAlreadyAdded.add(var.name)) {
79                         completions.add(createLocalValueCompletion(request.position, prefix, declarations, moduleName, var.name, valueType.type));
80                         if(maxNumberOfCompletions.decrementAndGet() <= 0)
81                             break localDefs;
82                     }
83                 }
84             }
85             for(String valueName : declarations.valueDefinitionsAst.getValueNames())
86                 if(valueName.toLowerCase().startsWith(prefix)  && valuesAlreadyAdded.add(valueName)) {
87                     completions.add(createLocalValueCompletion(request.position, prefix, declarations, moduleName, valueName, null));
88                     if(maxNumberOfCompletions.decrementAndGet() <= 0)
89                         break localDefs;
90                 }
91         }
92         
93         // Find completions from dependencies
94         ArrayList<ImportDeclaration> imports = Elaboration.processRelativeImports(null,
95                 request.moduleSource.getModuleName(), declarations.importsAst);
96         //for(ImportDeclaration import_ : imports)
97         //    LOGGER.debug("    import {} as {}", import_.moduleName, import_.localName);
98         
99         EnvironmentFactory environmentFactory = new EnvironmentFactoryImpl(request.repository,
100                 request.moduleSource.getBuiltinImports(null), null);
101         Environment environment = environmentFactory.createEnvironmentRobustly(
102                 context,
103                 imports.toArray(new ImportDeclaration[imports.size()]));
104         
105         Namespace namespace = environment.getLocalNamespace();
106         int pos = 0;
107         while(true) {
108             int p = prefix.indexOf('.', pos);
109             if(p == -1) {
110                 prefix = prefix.substring(pos);
111                 break;
112             }
113             String namespaceName = prefix.substring(pos, p);
114             Namespace temp = namespace.getNamespace(namespaceName);
115             //LOGGER.debug("    found namespace {}", namespaceName);
116             
117             if(temp == null) {
118                 prefix = prefix.substring(pos);
119                 break;
120             }
121             else {
122                 namespace = temp;
123                 pos = p+1;
124             }
125         }
126         String prefix_ = prefix;
127         //LOGGER.debug("    prefix = {}", prefix);
128         
129         THashSet<String> namespacesAlreadyAdded = new THashSet<String>();
130         namespacesAlreadyAdded.add("");
131         
132         for(String localNamespace : namespace.getNamespaces())
133             if(localNamespace.toLowerCase().startsWith(prefix) && namespacesAlreadyAdded.add(localNamespace))
134                 completions.add(createNamespaceCompletion(request.position, prefix, localNamespace));
135                 
136         namespace.findTypesForPrefix(prefix, AcceptAllNamespaceFilter.INSTANCE, (TCon type) -> {
137             completions.add(createImportedTypeCompletion(request.position, prefix_, type));
138             maxNumberOfCompletions.decrementAndGet();
139         });
140         // TODO effects
141         namespace.findValuesForPrefix(prefix, AcceptAllNamespaceFilter.INSTANCE, new TObjectProcedure<SCLValue>() {
142             @Override
143             public boolean execute(SCLValue value) {
144                 String name = value.getName().name;
145                 int dp = name.indexOf('.', prefix_.length());
146                 if(dp != -1) {
147                     String localNamespace = name.substring(0, dp);
148                     if(namespacesAlreadyAdded.add(localNamespace)) {
149                         completions.add(createNamespaceCompletion(request.position, prefix_, localNamespace));
150                         return maxNumberOfCompletions.decrementAndGet() > 0;
151                     }
152                     else
153                         return true;
154                 }
155                 else {
156                     completions.add(createImportedValueCompletion(request.position, prefix_, value));
157                     return maxNumberOfCompletions.decrementAndGet() > 0;
158                 }
159             }
160         });
161         
162         Collections.sort(completions, new CompletionComparator());
163         return completions;
164     }
165     
166     private static Completion createNamespaceCompletion(int position, String prefix, String localNamespace) {
167         int length = prefix.length();
168         int begin = position - length;
169         return new Completion(position-length, length, localNamespace, begin + localNamespace.length(),
170                 CompletionType.Namespace, null, localNamespace, null, null);
171     }
172
173     private static Completion createLocalValueCompletion(int position, String prefix, DeclarationClassification declarations,
174             String moduleName, String valueName, TypeAst type) {
175         int length = prefix.length();
176         int begin = position - length;
177         DDocumentationAst documentationAst = declarations.valueDocumentation.get(valueName);
178         String documentation = documentationAst!=null ? documentationAst.documentation : null;
179         return new Completion(position-length, length, valueName, begin + valueName.length(),
180                 CompletionType.Value, moduleName, valueName, type != null ? type.toString() : null, documentation);
181     }
182
183     private static Completion createLocalTypeCompletion(int position, String prefix, DeclarationClassification declarations,
184             String moduleName, String typeName) {
185         int length = prefix.length();
186         int begin = position - length;
187         return new Completion(position-length, length, typeName, begin + typeName.length(),
188                 CompletionType.Type, moduleName, typeName, null, null);
189     }
190     
191     private static Completion createImportedValueCompletion(int position, String prefix, SCLValue value) {
192         int length = prefix.length();
193         int begin = position - length;
194         String documentation = value.documentation;
195         Name name = value.getName();
196         return new Completion(position-length, length, name.name, begin + name.name.length(),
197                 CompletionType.Value, name.module, name.name, value.getType().toString(), documentation);
198     }
199
200     private static Completion createImportedTypeCompletion(int position, String prefix, TCon type) {
201         int length = prefix.length();
202         int begin = position - length;
203         return new Completion(position-length, length, type.name, begin + type.name.length(),
204                 CompletionType.Type, type.module, type.name, null, "");
205     }
206
207 }