--- /dev/null
+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, "");
+ }
+
+}