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 findCompletions(CompletionRequest request) { //LOGGER.debug("findCompletions"); AtomicInteger maxNumberOfCompletions = new AtomicInteger(request.maxNumberOfCompletions); ArrayList completions = new ArrayList(); 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 valuesAlreadyAdded = new THashSet(); 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 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 namespacesAlreadyAdded = new THashSet(); 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() { @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, ""); } }