]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - 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
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/Completions.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/Completions.java
new file mode 100644 (file)
index 0000000..aa435c6
--- /dev/null
@@ -0,0 +1,207 @@
+package org.simantics.scl.compiler.completions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.simantics.scl.compiler.common.names.Name;
+import org.simantics.scl.compiler.compilation.CompilationContext;
+import org.simantics.scl.compiler.compilation.DeclarationClassification;
+import org.simantics.scl.compiler.compilation.Elaboration;
+import org.simantics.scl.compiler.completions.parsing.RobustModuleParser;
+import org.simantics.scl.compiler.elaboration.expressions.EVar;
+import org.simantics.scl.compiler.elaboration.modules.SCLValue;
+import org.simantics.scl.compiler.environment.Environment;
+import org.simantics.scl.compiler.environment.EnvironmentFactory;
+import org.simantics.scl.compiler.environment.EnvironmentFactoryImpl;
+import org.simantics.scl.compiler.environment.Namespace;
+import org.simantics.scl.compiler.environment.filter.AcceptAllNamespaceFilter;
+import org.simantics.scl.compiler.internal.parsing.declarations.ConstructorAst;
+import org.simantics.scl.compiler.internal.parsing.declarations.DDataAst;
+import org.simantics.scl.compiler.internal.parsing.declarations.DDocumentationAst;
+import org.simantics.scl.compiler.internal.parsing.declarations.DTypeAst;
+import org.simantics.scl.compiler.internal.parsing.declarations.DValueTypeAst;
+import org.simantics.scl.compiler.internal.parsing.types.TypeAst;
+import org.simantics.scl.compiler.module.ImportDeclaration;
+import org.simantics.scl.compiler.types.TCon;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import gnu.trove.procedure.TObjectProcedure;
+import gnu.trove.set.hash.THashSet;
+
+public class Completions {
+    private static final Logger LOGGER = LoggerFactory.getLogger(Completions.class);
+    
+    public static List<Completion> findCompletions(CompletionRequest request) {
+        //LOGGER.debug("findCompletions");
+        AtomicInteger maxNumberOfCompletions = new AtomicInteger(request.maxNumberOfCompletions);
+        ArrayList<Completion> completions = new ArrayList<Completion>();
+        
+        String moduleName = request.moduleSource.getModuleName();
+        CompilationContext context = new CompilationContext();
+        
+        String prefix = PrefixUtil.findPrefix(request.sourceText, request.position).toLowerCase();
+        //LOGGER.debug("    prefix = {}", prefix);
+        
+        DeclarationClassification declarations = RobustModuleParser.parse(context, request.sourceText);
+
+        
+        // Find local completions
+        localDefs: if(!prefix.contains(".")) {
+            THashSet<String> valuesAlreadyAdded = new THashSet<String>(); 
+            for(DDataAst dataAst : declarations.dataTypesAst) {
+                if(dataAst.name.toLowerCase().startsWith(prefix)) {
+                    completions.add(createLocalTypeCompletion(request.position, prefix, declarations, moduleName, dataAst.name));
+                    if(maxNumberOfCompletions.decrementAndGet() <= 0)
+                        break localDefs;
+                }
+                for(ConstructorAst constructor : dataAst.constructors) {
+                    if(constructor.name.text.toLowerCase().startsWith(prefix) && valuesAlreadyAdded.add(constructor.name.text)) {
+                        completions.add(createLocalValueCompletion(request.position, prefix, declarations, moduleName, constructor.name.text, null));
+                        if(maxNumberOfCompletions.decrementAndGet() <= 0)
+                            break localDefs;
+                    }
+                }
+            }
+            for(DTypeAst typeAst : declarations.typeAliasesAst) {
+                if(typeAst.name.toLowerCase().startsWith(prefix)) {
+                    completions.add(createLocalTypeCompletion(request.position, prefix, declarations, moduleName, typeAst.name));
+                    if(maxNumberOfCompletions.decrementAndGet() <= 0)
+                        break localDefs;
+                }
+            }
+            
+            for(DValueTypeAst valueType : declarations.typeAnnotationsAst) {
+                for(EVar var : valueType.names) {
+                    if(var.name.toLowerCase().startsWith(prefix) && valuesAlreadyAdded.add(var.name)) {
+                        completions.add(createLocalValueCompletion(request.position, prefix, declarations, moduleName, var.name, valueType.type));
+                        if(maxNumberOfCompletions.decrementAndGet() <= 0)
+                            break localDefs;
+                    }
+                }
+            }
+            for(String valueName : declarations.valueDefinitionsAst.getValueNames())
+                if(valueName.toLowerCase().startsWith(prefix)  && valuesAlreadyAdded.add(valueName)) {
+                    completions.add(createLocalValueCompletion(request.position, prefix, declarations, moduleName, valueName, null));
+                    if(maxNumberOfCompletions.decrementAndGet() <= 0)
+                        break localDefs;
+                }
+        }
+        
+        // Find completions from dependencies
+        ArrayList<ImportDeclaration> imports = Elaboration.processRelativeImports(null,
+                request.moduleSource.getModuleName(), declarations.importsAst);
+        //for(ImportDeclaration import_ : imports)
+        //    LOGGER.debug("    import {} as {}", import_.moduleName, import_.localName);
+        
+        EnvironmentFactory environmentFactory = new EnvironmentFactoryImpl(request.repository,
+                request.moduleSource.getBuiltinImports(null), null);
+        Environment environment = environmentFactory.createEnvironmentRobustly(
+                context,
+                imports.toArray(new ImportDeclaration[imports.size()]));
+        
+        Namespace namespace = environment.getLocalNamespace();
+        int pos = 0;
+        while(true) {
+            int p = prefix.indexOf('.', pos);
+            if(p == -1) {
+                prefix = prefix.substring(pos);
+                break;
+            }
+            String namespaceName = prefix.substring(pos, p);
+            Namespace temp = namespace.getNamespace(namespaceName);
+            //LOGGER.debug("    found namespace {}", namespaceName);
+            
+            if(temp == null) {
+                prefix = prefix.substring(pos);
+                break;
+            }
+            else {
+                namespace = temp;
+                pos = p+1;
+            }
+        }
+        String prefix_ = prefix;
+        //LOGGER.debug("    prefix = {}", prefix);
+        
+        THashSet<String> namespacesAlreadyAdded = new THashSet<String>();
+        namespacesAlreadyAdded.add("");
+        
+        for(String localNamespace : namespace.getNamespaces())
+            if(localNamespace.toLowerCase().startsWith(prefix) && namespacesAlreadyAdded.add(localNamespace))
+                completions.add(createNamespaceCompletion(request.position, prefix, localNamespace));
+                
+        namespace.findTypesForPrefix(prefix, AcceptAllNamespaceFilter.INSTANCE, (TCon type) -> {
+            completions.add(createImportedTypeCompletion(request.position, prefix_, type));
+            maxNumberOfCompletions.decrementAndGet();
+        });
+        // TODO effects
+        namespace.findValuesForPrefix(prefix, AcceptAllNamespaceFilter.INSTANCE, new TObjectProcedure<SCLValue>() {
+            @Override
+            public boolean execute(SCLValue value) {
+                String name = value.getName().name;
+                int dp = name.indexOf('.', prefix_.length());
+                if(dp != -1) {
+                    String localNamespace = name.substring(0, dp);
+                    if(namespacesAlreadyAdded.add(localNamespace)) {
+                        completions.add(createNamespaceCompletion(request.position, prefix_, localNamespace));
+                        return maxNumberOfCompletions.decrementAndGet() > 0;
+                    }
+                    else
+                        return true;
+                }
+                else {
+                    completions.add(createImportedValueCompletion(request.position, prefix_, value));
+                    return maxNumberOfCompletions.decrementAndGet() > 0;
+                }
+            }
+        });
+        
+        Collections.sort(completions, new CompletionComparator());
+        return completions;
+    }
+    
+    private static Completion createNamespaceCompletion(int position, String prefix, String localNamespace) {
+        int length = prefix.length();
+        int begin = position - length;
+        return new Completion(position-length, length, localNamespace, begin + localNamespace.length(),
+                CompletionType.Namespace, null, localNamespace, null, null);
+    }
+
+    private static Completion createLocalValueCompletion(int position, String prefix, DeclarationClassification declarations,
+            String moduleName, String valueName, TypeAst type) {
+        int length = prefix.length();
+        int begin = position - length;
+        DDocumentationAst documentationAst = declarations.valueDocumentation.get(valueName);
+        String documentation = documentationAst!=null ? documentationAst.documentation : null;
+        return new Completion(position-length, length, valueName, begin + valueName.length(),
+                CompletionType.Value, moduleName, valueName, type != null ? type.toString() : null, documentation);
+    }
+
+    private static Completion createLocalTypeCompletion(int position, String prefix, DeclarationClassification declarations,
+            String moduleName, String typeName) {
+        int length = prefix.length();
+        int begin = position - length;
+        return new Completion(position-length, length, typeName, begin + typeName.length(),
+                CompletionType.Type, moduleName, typeName, null, null);
+    }
+    
+    private static Completion createImportedValueCompletion(int position, String prefix, SCLValue value) {
+        int length = prefix.length();
+        int begin = position - length;
+        String documentation = value.documentation;
+        Name name = value.getName();
+        return new Completion(position-length, length, name.name, begin + name.name.length(),
+                CompletionType.Value, name.module, name.name, value.getType().toString(), documentation);
+    }
+
+    private static Completion createImportedTypeCompletion(int position, String prefix, TCon type) {
+        int length = prefix.length();
+        int begin = position - length;
+        return new Completion(position-length, length, type.name, begin + type.name.length(),
+                CompletionType.Type, type.module, type.name, null, "");
+    }
+
+}