1 package org.simantics.scl.compiler.completions;
3 import java.util.ArrayList;
4 import java.util.Collections;
6 import java.util.concurrent.atomic.AtomicInteger;
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;
31 import gnu.trove.procedure.TObjectProcedure;
32 import gnu.trove.set.hash.THashSet;
34 public class Completions {
35 private static final Logger LOGGER = LoggerFactory.getLogger(Completions.class);
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>();
42 String moduleName = request.moduleSource.getModuleName();
43 CompilationContext context = new CompilationContext();
45 String prefix = PrefixUtil.findPrefix(request.sourceText, request.position).toLowerCase();
46 //LOGGER.debug(" prefix = {}", prefix);
48 DeclarationClassification declarations = RobustModuleParser.parse(context, request.sourceText);
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)
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)
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)
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)
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)
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);
99 EnvironmentFactory environmentFactory = new EnvironmentFactoryImpl(request.repository,
100 request.moduleSource.getBuiltinImports(null), null);
101 Environment environment = environmentFactory.createEnvironmentRobustly(
103 imports.toArray(new ImportDeclaration[imports.size()]));
105 Namespace namespace = environment.getLocalNamespace();
108 int p = prefix.indexOf('.', pos);
110 prefix = prefix.substring(pos);
113 String namespaceName = prefix.substring(pos, p);
114 Namespace temp = namespace.getNamespace(namespaceName);
115 //LOGGER.debug(" found namespace {}", namespaceName);
118 prefix = prefix.substring(pos);
126 String prefix_ = prefix;
127 //LOGGER.debug(" prefix = {}", prefix);
129 THashSet<String> namespacesAlreadyAdded = new THashSet<String>();
130 namespacesAlreadyAdded.add("");
132 for(String localNamespace : namespace.getNamespaces())
133 if(localNamespace.toLowerCase().startsWith(prefix) && namespacesAlreadyAdded.add(localNamespace))
134 completions.add(createNamespaceCompletion(request.position, prefix, localNamespace));
136 namespace.findTypesForPrefix(prefix, AcceptAllNamespaceFilter.INSTANCE, (TCon type) -> {
137 completions.add(createImportedTypeCompletion(request.position, prefix_, type));
138 maxNumberOfCompletions.decrementAndGet();
141 namespace.findValuesForPrefix(prefix, AcceptAllNamespaceFilter.INSTANCE, new TObjectProcedure<SCLValue>() {
143 public boolean execute(SCLValue value) {
144 String name = value.getName().name;
145 int dp = name.indexOf('.', prefix_.length());
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;
156 completions.add(createImportedValueCompletion(request.position, prefix_, value));
157 return maxNumberOfCompletions.decrementAndGet() > 0;
162 Collections.sort(completions, new CompletionComparator());
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);
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);
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);
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);
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, "");