New SCL completion implementation 56/1256/1
authorHannu Niemistö <hannu.niemisto@semantum.fi>
Thu, 23 Nov 2017 11:19:00 +0000 (13:19 +0200)
committerHannu Niemistö <hannu.niemisto@semantum.fi>
Thu, 23 Nov 2017 11:28:02 +0000 (13:28 +0200)
refs #7638

Change-Id: I57d2ddc1628ff8da2421c4eda41ce5b83187543e

36 files changed:
bundles/org.simantics.scl.compiler/META-INF/MANIFEST.MF
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/DeclarationClassification.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/Elaboration.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/NamespaceOfModule.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/Completion.java [new file with mode: 0644]
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionComparator.java [new file with mode: 0644]
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionRequest.java [new file with mode: 0644]
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionType.java [new file with mode: 0644]
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/Completions.java [new file with mode: 0644]
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/PrefixUtil.java [new file with mode: 0644]
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/ModuleSegment.java [new file with mode: 0644]
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/RobustModuleParser.java [new file with mode: 0644]
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/RobustModuleSplitter.java [new file with mode: 0644]
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/SubstringReader.java [new file with mode: 0644]
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/java/Builtins.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/modules/SCLValue.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EmptyEnvironment.java [new file with mode: 0644]
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EmptyNamespace.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EnvironmentFactory.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EnvironmentFactoryImpl.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/Namespace.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/NamespaceImpl.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/declarations/DAnnotationAst.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLParserImpl.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLPostLexer.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/ConcreteModule.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/ClassModuleSource.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/FileModuleSource.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/TextualModuleSource.java
bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/internal/BundleModuleSource.java
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/TestBase.java
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/TestClassNaming.java
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/SplittingExample1.scl [new file with mode: 0644]
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/TestPrefixUtil.java [new file with mode: 0644]
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/TestRobustModuleSplitter.java [new file with mode: 0644]

index a09f40cd504958f94ffb8ae4b72c18aa709a411e..7f76ebc102a775b01eba28a73ea0e300332e430d 100644 (file)
@@ -18,6 +18,8 @@ Export-Package: org.cojen.classfile,
  org.simantics.scl.compiler.common.names,
  org.simantics.scl.compiler.common.precedence,
  org.simantics.scl.compiler.compilation,
+ org.simantics.scl.compiler.completions,
+ org.simantics.scl.compiler.completions.parsing,
  org.simantics.scl.compiler.constants,
  org.simantics.scl.compiler.constants.generic,
  org.simantics.scl.compiler.elaboration.chr,
index 4bf5d847e8d8d745cd62c294ec51e8d786dbf6c2..ed21f04b2951c9f8bbd8e16ae75c3c7c114c3669 100644 (file)
@@ -39,37 +39,37 @@ import org.simantics.scl.compiler.module.ImportDeclaration;
 import gnu.trove.map.hash.THashMap;
 
 public class DeclarationClassification {
-    ArrayList<ImportDeclaration> importsAst = new ArrayList<ImportDeclaration>();
-    ArrayList<DDataAst> dataTypesAst = new ArrayList<DDataAst>();
-    ArrayList<DTypeAst> typeAliasesAst = new ArrayList<DTypeAst>();
-    ValueRepository valueDefinitionsAst = new ValueRepository(); 
-    RelationRepository relationDefinitionsAst = new RelationRepository();
-    ArrayList<DValueTypeAst> typeAnnotationsAst = new ArrayList<DValueTypeAst>();
-    ArrayList<DFixityAst> fixityAst = new ArrayList<DFixityAst>();
-    ArrayList<ProcessedDClassAst> typeClassesAst = new ArrayList<ProcessedDClassAst>();
-    ArrayList<ProcessedDInstanceAst> instancesAst = new ArrayList<ProcessedDInstanceAst>();
-    ArrayList<DDerivingInstanceAst> derivingInstancesAst = new ArrayList<DDerivingInstanceAst>();
-    ArrayList<DEffectAst> effectsAst = new ArrayList<DEffectAst>();
-    ArrayList<DRuleAst> rulesAst = new ArrayList<DRuleAst>();
-    ArrayList<DMappingRelationAst> mappingRelationsAst = new ArrayList<DMappingRelationAst>();
-    ArrayList<DRulesetAst> rulesetsAst = new ArrayList<DRulesetAst>();
+    public ArrayList<ImportDeclaration> importsAst = new ArrayList<ImportDeclaration>();
+    public ArrayList<DDataAst> dataTypesAst = new ArrayList<DDataAst>();
+    public ArrayList<DTypeAst> typeAliasesAst = new ArrayList<DTypeAst>();
+    public ValueRepository valueDefinitionsAst = new ValueRepository(); 
+    public RelationRepository relationDefinitionsAst = new RelationRepository();
+    public ArrayList<DValueTypeAst> typeAnnotationsAst = new ArrayList<DValueTypeAst>();
+    public ArrayList<DFixityAst> fixityAst = new ArrayList<DFixityAst>();
+    public ArrayList<ProcessedDClassAst> typeClassesAst = new ArrayList<ProcessedDClassAst>();
+    public ArrayList<ProcessedDInstanceAst> instancesAst = new ArrayList<ProcessedDInstanceAst>();
+    public ArrayList<DDerivingInstanceAst> derivingInstancesAst = new ArrayList<DDerivingInstanceAst>();
+    public ArrayList<DEffectAst> effectsAst = new ArrayList<DEffectAst>();
+    public ArrayList<DRuleAst> rulesAst = new ArrayList<DRuleAst>();
+    public ArrayList<DMappingRelationAst> mappingRelationsAst = new ArrayList<DMappingRelationAst>();
+    public ArrayList<DRulesetAst> rulesetsAst = new ArrayList<DRulesetAst>();
     
-    THashMap<String, DDocumentationAst> valueDocumentation = new THashMap<String, DDocumentationAst>();
-    THashMap<String, DDocumentationAst> relationDocumentation = new THashMap<String, DDocumentationAst>();
-    THashMap<String, DDocumentationAst> typeDocumentation = new THashMap<String, DDocumentationAst>();
-    THashMap<String, DDocumentationAst> classDocumentation = new THashMap<String, DDocumentationAst>();
+    public THashMap<String, DDocumentationAst> valueDocumentation = new THashMap<String, DDocumentationAst>();
+    public THashMap<String, DDocumentationAst> relationDocumentation = new THashMap<String, DDocumentationAst>();
+    public THashMap<String, DDocumentationAst> typeDocumentation = new THashMap<String, DDocumentationAst>();
+    public THashMap<String, DDocumentationAst> classDocumentation = new THashMap<String, DDocumentationAst>();
     
-    ArrayList<JavaMethodDeclaration> javaMethodDeclarations = new ArrayList<JavaMethodDeclaration>();
+    public ArrayList<JavaMethodDeclaration> javaMethodDeclarations = new ArrayList<JavaMethodDeclaration>();
     
-    StringBuilder moduleDocumentation = new StringBuilder();
+    public StringBuilder moduleDocumentation = new StringBuilder();
     
-    ArrayList<DAnnotationAst> currentAnnotations = new ArrayList<DAnnotationAst>(2); 
-    DDocumentationAst documentation;
-    String inJavaClass;
+    public ArrayList<DAnnotationAst> currentAnnotations = new ArrayList<DAnnotationAst>(2); 
+    public DDocumentationAst documentation;
+    public String inJavaClass;
     
-    ArrayList<DAnnotationAst> defaultAnnotations = new ArrayList<DAnnotationAst>();
+    public ArrayList<DAnnotationAst> defaultAnnotations = new ArrayList<DAnnotationAst>();
     
-    ErrorLog errorLog;
+    public ErrorLog errorLog;
     
     public DeclarationClassification(CompilationContext compilationContext) {
         this.errorLog = compilationContext.errorLog;
@@ -190,7 +190,7 @@ public class DeclarationClassification {
         importsAst.add(declaration);        
     }
     
-    public void handle(DImportJavaAst declaration) {        
+    public void handle(DImportJavaAst declaration) {
         if(!currentAnnotations.isEmpty()) {
             for(DAnnotationAst annotation : currentAnnotations) {
                 String name = annotation.id.text;
index 26bd31ec26cb96beebcac814e8dbce8c49b05212..799b0726819f3d6704f982ebfa0723e20fceb7e9 100644 (file)
@@ -79,6 +79,7 @@ import org.simantics.scl.compiler.internal.deriving.InstanceDerivers;
 import org.simantics.scl.compiler.internal.elaboration.profiling.BranchPointInjector;
 import org.simantics.scl.compiler.internal.elaboration.utils.StronglyConnectedComponents;
 import org.simantics.scl.compiler.internal.header.ModuleHeader;
+import org.simantics.scl.compiler.internal.parsing.Token;
 import org.simantics.scl.compiler.internal.parsing.declarations.ConstructorAst;
 import org.simantics.scl.compiler.internal.parsing.declarations.DAnnotationAst;
 import org.simantics.scl.compiler.internal.parsing.declarations.DClassAst;
@@ -95,6 +96,7 @@ import org.simantics.scl.compiler.internal.parsing.declarations.DTypeAst;
 import org.simantics.scl.compiler.internal.parsing.declarations.DValueAst;
 import org.simantics.scl.compiler.internal.parsing.declarations.DValueTypeAst;
 import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException;
+import org.simantics.scl.compiler.internal.parsing.parser.SCLTerminals;
 import org.simantics.scl.compiler.internal.parsing.translation.ProcessedDClassAst;
 import org.simantics.scl.compiler.internal.parsing.translation.ProcessedDInstanceAst;
 import org.simantics.scl.compiler.internal.parsing.translation.RelationRepository;
@@ -156,7 +158,8 @@ public class Elaboration {
         this.errorLog = compilationContext.errorLog;
         this.moduleName = moduleName;
         this.moduleHeader = moduleHeader;
-        importsAst = processRelativeImports(importsAst);
+        if(moduleName != null)
+            importsAst = processRelativeImports(compilationContext.errorLog, moduleName, importsAst);
         this.importsAst = importsAst;
         this.jrvFactory = jrvFactory;
         this.javaReferenceValidator = moduleHeader == null || moduleHeader.classLoader == null
@@ -200,7 +203,7 @@ public class Elaboration {
         compilationContext.namingPolicy = new JavaNamingPolicy(moduleName);
     }
     
-    private ArrayList<ImportDeclaration> processRelativeImports(ArrayList<ImportDeclaration> relativeImports) {
+    public static ArrayList<ImportDeclaration> processRelativeImports(ErrorLog errorLog, String moduleName, ArrayList<ImportDeclaration> relativeImports) {
         ArrayList<ImportDeclaration> absoluteImports = new ArrayList<ImportDeclaration>(relativeImports.size());
         for(ImportDeclaration relativeImport : relativeImports) {
             if(relativeImport.moduleName.startsWith(".")) {
@@ -212,7 +215,8 @@ public class Elaboration {
                             relativeImport.reexport, relativeImport.spec);
                     absoluteImports.add(absoluteImport);
                                } catch (InvalidModulePathException e) {
-                                       errorLog.log(relativeImport.location, e.getMessage());
+                                   if(errorLog != null)
+                                       errorLog.log(relativeImport.location, e.getMessage());
                                }
             }
             else
@@ -524,9 +528,8 @@ public class Elaboration {
                 method.setDefaultImplementation(Name.create(moduleName, fullName));
                 
                 valueDefinitionsAst.addDefinitions(fullName, defs);
-                /*valueDefinitionsAst.addAnnotation(fullName, new DAnnotationAst(new EVar("@private"), 
-                        Collections.<Expression>emptyList()));*/
-                supplementedTypeAnnotations.add(new SupplementedValueType(defs.get(0).location, fullName, method.getType()));                
+                supplementedTypeAnnotations.add(new SupplementedValueType(defs.get(0).location, fullName, method.getType()));
+                valueDefinitionsAst.setDerived(fullName);
             }
             
             module.addTypeClass(classAst.name, typeClass);
index 8621283b73dc5e12bb7a1c5473d36cc438518567..4de6cac816477e79dfe9db5917fccbf4652b9a75 100644 (file)
@@ -1,6 +1,7 @@
 package org.simantics.scl.compiler.compilation;
 
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.function.Consumer;
 
 import org.simantics.scl.compiler.elaboration.chr.CHRRuleset;
@@ -35,6 +36,11 @@ public class NamespaceOfModule implements Namespace {
     public Namespace getNamespace(String name) {
         return base.getNamespace(name);
     }
+    
+    @Override
+    public Collection<String> getNamespaces() {
+        return base.getNamespaces();
+    }
 
     @Override
     public SCLValue getValue(String name) throws AmbiguousNameException {
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/Completion.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/Completion.java
new file mode 100644 (file)
index 0000000..dc21c6c
--- /dev/null
@@ -0,0 +1,32 @@
+package org.simantics.scl.compiler.completions;
+
+public class Completion {
+    public final int startOfReplacedText;
+    public final int lengthOfReplacedText;
+    public final String replacement;
+    public final int cursorPositionAfterReplacement;
+    
+    public final CompletionType completionType;
+    public final String module;
+    public final String name;
+    public final String type; // may be null
+    public final String documentation; // may be null
+    
+    public Completion(int startOfReplacedText, int lengthOfReplacedText, String replacement,
+            int cursorPositionAfterReplacement, CompletionType completionType, String definingModule, String name,
+            String type, String documentation) {
+        this.startOfReplacedText = startOfReplacedText;
+        this.lengthOfReplacedText = lengthOfReplacedText;
+        this.replacement = replacement;
+        this.cursorPositionAfterReplacement = cursorPositionAfterReplacement;
+        this.completionType = completionType;
+        this.module = definingModule;
+        this.name = name;
+        this.type = type;
+        this.documentation = "".equals(documentation) ? null : documentation;
+    }
+    
+    public String getLabel() {
+        return name + (type != null ? " :: " + type : "") + (module != null ? " (" + module + ")" : "") ;
+    }
+}
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionComparator.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionComparator.java
new file mode 100644 (file)
index 0000000..aff7552
--- /dev/null
@@ -0,0 +1,12 @@
+package org.simantics.scl.compiler.completions;
+
+import java.util.Comparator;
+
+public class CompletionComparator implements Comparator<Completion> {
+
+    @Override
+    public int compare(Completion o1, Completion o2) {
+        return o1.name.toLowerCase().compareTo(o2.name.toLowerCase());
+    }
+
+}
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionRequest.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionRequest.java
new file mode 100644 (file)
index 0000000..84a828f
--- /dev/null
@@ -0,0 +1,43 @@
+package org.simantics.scl.compiler.completions;
+
+import org.simantics.scl.compiler.environment.EnvironmentFactory;
+import org.simantics.scl.compiler.module.repository.ModuleRepository;
+import org.simantics.scl.compiler.source.TextualModuleSource;
+
+public class CompletionRequest {
+    public static final int DEFAULT_MAX_NUMBER_OF_COMPLETIONS = 1000;
+    
+    public ModuleRepository repository;
+
+    public TextualModuleSource moduleSource;
+    
+    /**
+     * Source text of the SCL module.
+     */
+    public String sourceText;
+    
+    /**
+     * Cursor position. Cursor is between characters (position-1) and position.
+     */
+    public int position;
+    
+    /**
+     * Maximum number of completions that will be returned
+     */
+    public int maxNumberOfCompletions;
+    public CompletionRequest() {
+        this.maxNumberOfCompletions = DEFAULT_MAX_NUMBER_OF_COMPLETIONS;
+    }
+    
+    public CompletionRequest(ModuleRepository repository, TextualModuleSource moduleSource, String sourceText, int position, int maxNumberOfCompletions) {
+        this.repository = repository;
+        this.moduleSource = moduleSource;
+        this.sourceText = sourceText;
+        this.position = position;
+        this.maxNumberOfCompletions = maxNumberOfCompletions;
+    }
+    
+    public CompletionRequest(ModuleRepository repository,  TextualModuleSource moduleSource, String sourceText, int position) {
+        this(repository, moduleSource, sourceText, position, DEFAULT_MAX_NUMBER_OF_COMPLETIONS);
+    }
+}
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionType.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionType.java
new file mode 100644 (file)
index 0000000..b69abb1
--- /dev/null
@@ -0,0 +1,7 @@
+package org.simantics.scl.compiler.completions;
+
+public enum CompletionType {
+    Value,
+    Type,
+    Namespace
+}
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, "");
+    }
+
+}
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/PrefixUtil.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/PrefixUtil.java
new file mode 100644 (file)
index 0000000..a4ce9ad
--- /dev/null
@@ -0,0 +1,37 @@
+package org.simantics.scl.compiler.completions;
+
+public class PrefixUtil {
+    public static String findPrefix(String sourceText, int end) {
+        int position;
+        for(position=end-1;position >= 0 && isPrefixChar(sourceText.charAt(position));--position);
+        ++position;
+        while(position < end && !isPrefixStart(sourceText.charAt(position)))
+            ++position;
+        return sourceText.substring(position, end);
+    }
+
+    private static boolean isPrefixStart(char c) {
+        return Character.isJavaIdentifierStart(c);
+    }
+
+    private static boolean isPrefixChar(char c) {
+        return Character.isJavaIdentifierPart(c) || c=='.';
+    }
+    
+    public static String[] splitPrefix(String prefix) {
+        int partCount = 1;
+        for(int i=0;i<prefix.length();++i)
+            if(prefix.charAt(i) == '.')
+                ++partCount;
+        String[] result = new String[partCount];
+        int partId = 0;
+        int begin = 0;
+        for(int i=0;i<prefix.length();++i)
+            if(prefix.charAt(i) == '.') {
+                result[partId++] = prefix.substring(begin, i);
+                begin = i+1;
+            }
+        result[partId] = prefix.substring(begin);
+        return result;
+    }
+}
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/ModuleSegment.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/ModuleSegment.java
new file mode 100644 (file)
index 0000000..4ef2dcd
--- /dev/null
@@ -0,0 +1,15 @@
+package org.simantics.scl.compiler.completions.parsing;
+
+public class ModuleSegment {
+    public final int begin;
+    public final int end;
+    public int parenthesesBalance;
+    public boolean hasErrors;
+    
+    public ModuleSegment(int begin, int end, int parenthesesBalance, boolean hasErrors) {
+        this.begin = begin;
+        this.end = end;
+        this.parenthesesBalance = parenthesesBalance;
+        this.hasErrors = hasErrors;
+    }
+}
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/RobustModuleParser.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/RobustModuleParser.java
new file mode 100644 (file)
index 0000000..e9f2ffb
--- /dev/null
@@ -0,0 +1,49 @@
+package org.simantics.scl.compiler.completions.parsing;
+
+import java.io.Reader;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.simantics.scl.compiler.compilation.CompilationContext;
+import org.simantics.scl.compiler.compilation.DeclarationClassification;
+import org.simantics.scl.compiler.internal.parsing.declarations.DeclarationAst;
+import org.simantics.scl.compiler.internal.parsing.parser.SCLParserImpl;
+import org.simantics.scl.compiler.internal.parsing.parser.SCLParserOptions;
+
+public class RobustModuleParser {
+    
+    private static List<DeclarationAst> parseSegment(CompilationContext context, AtomicBoolean firstToken, Reader reader) {
+        SCLParserImpl parser = new SCLParserImpl(reader);
+        parser.getLexer().isFirstToken = firstToken.get();
+        parser.setCompilationContext(context);
+        parser.setParserOptions(SCLParserOptions.MODULE_DEFAULT);
+        List<DeclarationAst> result = (List<DeclarationAst>)parser.parseModule();
+        firstToken.set(parser.getLexer().isFirstToken);
+        return result;
+    }
+
+    public static DeclarationClassification parse(CompilationContext context, String sourceText) {
+        DeclarationClassification declarations = new DeclarationClassification(context);
+        AtomicBoolean firstToken = new AtomicBoolean(false);
+        for(ModuleSegment segment : RobustModuleSplitter.split(sourceText)) {
+            //System.out.println("----------------------------------------------------------");
+            //System.out.println(sourceText.substring(segment.begin, segment.end));
+
+            if(segment.hasErrors) {
+                System.out.println("    has errors");
+                // TODO we could try some special parsing here
+            }
+            else {
+                Reader reader = new SubstringReader(sourceText, segment.begin, segment.end);
+                try {
+                    for(DeclarationAst declaration : parseSegment(context, firstToken, reader))
+                        declarations.handle(declaration);
+                } catch(Exception e) {
+                    // This is normal
+                    //e.printStackTrace(System.out);
+                }
+            }
+        }
+        return declarations;
+    }
+}
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/RobustModuleSplitter.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/RobustModuleSplitter.java
new file mode 100644 (file)
index 0000000..283af61
--- /dev/null
@@ -0,0 +1,254 @@
+package org.simantics.scl.compiler.completions.parsing;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RobustModuleSplitter {
+    // state ids
+    private static final int NORMAL_START_OF_LINE   = 0;
+    private static final int NORMAL                 = 1;
+    private static final int NORMAL_1QUOTE          = 2;
+    private static final int NORMAL_2QUOTE          = 3;
+    private static final int SHORT_STRING           = 4;
+    private static final int SHORT_STRING_BACKSLASH = 5;
+    private static final int LONG_STRING            = 6;
+    private static final int LONG_STRING_1QUOTE     = 7;
+    private static final int LONG_STRING_2QUOTE     = 8;
+    private static final int CHAR_LITERAL           = 9;
+    private static final int CHAR_LITERAL_BACKSLASH = 10;
+    private static final int NORMAL_1SLASH          = 11;
+    private static final int C_COMMENT              = 12;
+    private static final int C_COMMENT_STAR         = 13;
+    private static final int CPP_COMMENT            = 14;
+    
+    private final String sourceText;
+    private ArrayList<ModuleSegment> segments = new ArrayList<ModuleSegment>();
+    
+    private RobustModuleSplitter(String sourceText) {
+        this.sourceText = sourceText;
+    }
+
+    private static boolean isLineEnd(char c) {
+        return c == '\n' || c == 0;
+    }
+    
+    private void split() {
+        int state = NORMAL;
+        int begin = 0, pos = 0, curEntityBegin = 0, parenthesesBalance = 0;
+        boolean hasErrors = false;
+        int length = sourceText.length();
+        loop: while(true) {
+            char c = pos == length ? 0 : sourceText.charAt(pos++);
+            if(c == '\r')
+                c = '\n';
+            switch(state) {
+            case NORMAL_START_OF_LINE:
+                if(c == '\n') // Don't care about empty lines
+                    break;
+                if(c != ' ') {
+                    int end = c == 0 ? pos : pos-1;
+                    segments.add(new ModuleSegment(begin, end, parenthesesBalance, hasErrors));
+                    parenthesesBalance = 0;
+                    hasErrors = false;
+                    begin = end;
+                }
+                state = NORMAL;
+            case NORMAL:
+                if(c == '"')
+                    state = NORMAL_1QUOTE;
+                else if(c == '/')
+                    state = NORMAL_1SLASH;
+                else if(c == '\'')
+                    state = CHAR_LITERAL;
+                else if(c == '(' || c == '[' || c == '{') 
+                    ++parenthesesBalance;
+                else if(c == ')' || c == ']' || c == '}') 
+                    --parenthesesBalance;
+                else if(c == '\n')
+                    state = NORMAL_START_OF_LINE;
+                else if(c == 0)
+                    break loop;
+                break;
+            case NORMAL_1QUOTE:
+                if(c == '"')
+                    state = NORMAL_2QUOTE;
+                else if(c == '\\')
+                    state = SHORT_STRING_BACKSLASH;
+                else if(c == 0)
+                    break loop;
+                else
+                    state = SHORT_STRING;
+                break;
+            case NORMAL_2QUOTE:
+                if(c == '"')
+                    state = LONG_STRING;
+                else {
+                    state = NORMAL;
+                    if(c != 0)
+                        --pos;
+                }
+                break;
+            case SHORT_STRING:
+                if(c == '\\')
+                    state = SHORT_STRING_BACKSLASH;
+                else if(c == '"' || isLineEnd(c) /* unclosed string */) {
+                    if(c == '\n')
+                        state = NORMAL_START_OF_LINE;
+                    else
+                        state = NORMAL;
+                    hasErrors = c != '"';
+                }
+                break;
+            case SHORT_STRING_BACKSLASH:
+                if(isLineEnd(c) /* unclosed string */)
+                    state = NORMAL_START_OF_LINE;
+                else
+                    state = SHORT_STRING;
+                break;
+            case LONG_STRING:
+                if(c == '"')
+                    state = LONG_STRING_1QUOTE;
+                else if(c == 0) {
+                    // Unclosed long string
+                    curEntityBegin = pos;
+                    state = NORMAL;
+                    hasErrors = true;
+                }
+                break;
+            case LONG_STRING_1QUOTE:
+                if(c == '"')
+                    state = LONG_STRING_2QUOTE;
+                else
+                    state = LONG_STRING;
+                break;
+            case LONG_STRING_2QUOTE:
+                if(c == '"')
+                    state = NORMAL;
+                else
+                    state = LONG_STRING;
+                break;
+            case CHAR_LITERAL:
+                if(c == '\'' || isLineEnd(c) /* unclosed char literal */) {
+                    if(c == '\n')
+                        state = NORMAL_START_OF_LINE;
+                    else
+                        state = NORMAL;
+                    hasErrors = c != '\'';
+                }
+                else if(c == '\\')
+                    state = CHAR_LITERAL_BACKSLASH;
+                break;
+            case CHAR_LITERAL_BACKSLASH:
+                if(isLineEnd(c) /* unclosed char literal */) {
+                    state = NORMAL_START_OF_LINE;
+                    hasErrors = true;
+                }
+                else
+                    state = CHAR_LITERAL;
+                break;
+            case NORMAL_1SLASH:
+                if(c == '/')
+                    state = CPP_COMMENT;
+                else if(c == '*') {
+                    state = C_COMMENT;
+                    curEntityBegin = pos;
+                }
+                else {
+                    state = NORMAL;
+                    if(c != 0)
+                        --pos;
+                }
+                break;
+            case C_COMMENT:
+                if(c == '*')
+                    state = C_COMMENT_STAR;
+                else if(c == 0) {
+                    // Unclosed C comment
+                    pos = curEntityBegin;
+                    state = NORMAL;
+                    hasErrors = true;
+                }
+                break;
+            case C_COMMENT_STAR:
+                if(c == '/') {
+                    state = NORMAL;
+                }
+                else
+                    state = C_COMMENT;
+                break;
+            case CPP_COMMENT:
+                if(isLineEnd(c))
+                    state = NORMAL_START_OF_LINE;
+                break;
+            }
+        }
+        if(begin != length)
+            segments.add(new ModuleSegment(begin, length, parenthesesBalance, hasErrors));
+    }
+    
+    private void combineByParenthesesBalance() {
+        ArrayList<ModuleSegment> segmentStack = null; 
+        for(ModuleSegment segment : segments)
+            if(segment.parenthesesBalance > 0) {
+                if(segmentStack == null)
+                    segmentStack = new ArrayList<ModuleSegment>();
+                for(int i=0;i<segment.parenthesesBalance;++i)
+                    segmentStack.add(segment);
+            }
+            else if(segment.parenthesesBalance < 0) {
+                if(segmentStack == null) {
+                    segment.parenthesesBalance = 0;
+                    segment.hasErrors = true;
+                }
+                else {
+                    int r = -segment.parenthesesBalance;
+                    while(r > 0 && !segmentStack.isEmpty()) {
+                        segmentStack.remove(segmentStack.size()-1);
+                        --r;
+                    }
+                    if(r > 0) {
+                        segment.parenthesesBalance += r;
+                        segment.hasErrors = true;
+                    }
+                }
+            }
+        if(segmentStack == null)
+            return;
+        for(ModuleSegment segment : segmentStack) {
+            --segment.parenthesesBalance;
+            segment.hasErrors = true;
+        }
+        
+        ArrayList<ModuleSegment> oldSegments = segments;
+        segments = new ArrayList<ModuleSegment>(oldSegments.size());
+        
+        int currentBalance = 0;
+        int begin = 0;
+        boolean hasErrors = false;
+        for(ModuleSegment segment : oldSegments) {
+            if(currentBalance == 0) {
+                if(segment.parenthesesBalance == 0)
+                    segments.add(segment);
+                else {
+                    begin = segment.begin;
+                    currentBalance = segment.parenthesesBalance;
+                    hasErrors = segment.hasErrors;
+                }
+            }
+            else {
+                currentBalance += segment.parenthesesBalance;
+                hasErrors |= segment.hasErrors;
+                if(currentBalance == 0)
+                    segments.add(new ModuleSegment(begin, segment.end, 0, hasErrors));
+            }
+        }
+    }
+    
+    public static List<ModuleSegment> split(String sourceText) {
+        RobustModuleSplitter splitter = new RobustModuleSplitter(sourceText);
+        splitter.split();
+        splitter.combineByParenthesesBalance();
+        return splitter.segments;
+    }
+}
+
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/SubstringReader.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/SubstringReader.java
new file mode 100644 (file)
index 0000000..b74a0b3
--- /dev/null
@@ -0,0 +1,74 @@
+package org.simantics.scl.compiler.completions.parsing;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * Based on java.io.StringReader.
+ */
+
+public class SubstringReader extends Reader {
+
+    private String str;
+    private int end;
+    private int pos;
+    private int mark;
+
+    public SubstringReader(String str, int begin, int end) {
+        this.str = str;
+        this.pos = begin;
+        this.mark = begin;
+        this.end = end;
+    }
+
+    public int read() throws IOException {
+        if (pos >= end)
+            return -1;
+        return str.charAt(pos++);
+    }
+
+    public int read(char cbuf[], int off, int len) throws IOException {
+        if ((off < 0) || (off > cbuf.length) || (len < 0) ||
+                ((off + len) > cbuf.length) || ((off + len) < 0))
+            throw new IndexOutOfBoundsException();
+        else if (len == 0)
+            return 0;
+        if (pos >= end)
+            return -1;
+        int n = Math.min(end - pos, len);
+        str.getChars(pos, pos + n, cbuf, off);
+        pos += n;
+        return n;
+    }
+
+    public long skip(long ns) throws IOException {
+        if (pos >= end)
+            return 0;
+        // Bound skip by beginning and end of the source
+        long n = Math.min(end - pos, ns);
+        n = Math.max(-pos, n);
+        pos += n;
+        return n;
+    }
+
+    public boolean ready() throws IOException {
+        return true;
+    }
+
+    public boolean markSupported() {
+        return true;
+    }
+
+    public void mark(int readAheadLimit) throws IOException {
+        if (readAheadLimit < 0)
+            throw new IllegalArgumentException("Read-ahead limit < 0");
+        mark = pos;
+    }
+
+    public void reset() throws IOException {
+        pos = mark;
+    }
+
+    public void close() {
+    }
+}
index 4061b86525afe895c40e35eec063cb19a864e2e2..e3b429133f2198f65aa4a6b97c75ac2e2b7189c2 100644 (file)
@@ -24,6 +24,7 @@ import org.simantics.scl.compiler.constants.singletons.TypeOfProxyConstant;
 import org.simantics.scl.compiler.constants.singletons.TypeProxyConstant;
 import org.simantics.scl.compiler.elaboration.fundeps.Fundep;
 import org.simantics.scl.compiler.elaboration.modules.Documentation;
+import org.simantics.scl.compiler.elaboration.modules.PrivateProperty;
 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
 import org.simantics.scl.compiler.elaboration.modules.TypeClass;
 import org.simantics.scl.compiler.errors.Locations;
@@ -154,10 +155,12 @@ public class Builtins extends ConcreteModule {
         // *** Lists ***
         
         for(int arity=0;arity<=Constants.MAX_LIST_LITERAL_LENGTH;++arity) {
-            LIST_CONSTRUCTORS[arity] = addValue("_list_literal_" + arity + "_",
+            SCLValue value = addValue("_list_literal_" + arity + "_",
                     arity == 0 ? new EmptyListConstructor() : 
                         new ListConstructor(arity)
                     );
+            value.addProperty(PrivateProperty.INSTANCE);
+            LIST_CONSTRUCTORS[arity] = value;
         }
         
         // *** Boolean ***
index 408eb01e561cf96fc5bf020b04e574cadf0e030d..0d4651d4852530452959897f87aca2a2f1b7f01d 100644 (file)
@@ -159,4 +159,11 @@ public final class SCLValue implements Typed {
        public String getDocumentation() {
                return documentation;
        }
+
+    public boolean isPrivateOrDerived() {
+        for(SCLValueProperty property : properties)
+            if(property == PrivateProperty.INSTANCE || property == DerivedProperty.INSTANCE)
+                return true;
+        return false;
+    }
 }
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EmptyEnvironment.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EmptyEnvironment.java
new file mode 100644 (file)
index 0000000..ff751e3
--- /dev/null
@@ -0,0 +1,69 @@
+package org.simantics.scl.compiler.environment;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.simantics.scl.compiler.common.names.Name;
+import org.simantics.scl.compiler.constants.Constant;
+import org.simantics.scl.compiler.elaboration.modules.SCLValue;
+import org.simantics.scl.compiler.elaboration.modules.TypeClass;
+import org.simantics.scl.compiler.elaboration.modules.TypeClassInstance;
+import org.simantics.scl.compiler.elaboration.modules.TypeDescriptor;
+import org.simantics.scl.compiler.elaboration.relations.SCLEntityType;
+import org.simantics.scl.compiler.elaboration.relations.SCLRelation;
+import org.simantics.scl.compiler.elaboration.rules.TransformationRule;
+import org.simantics.scl.compiler.internal.codegen.effects.EffectConstructor;
+import org.simantics.scl.compiler.types.TCon;
+
+public enum EmptyEnvironment implements Environment {
+    INSTANCE;
+
+    @Override
+    public Namespace getLocalNamespace() {
+        return EmptyNamespace.INSTANCE;
+    }
+
+    @Override
+    public SCLValue getValue(Name name) {
+        return null;
+    }
+
+    @Override
+    public List<Constant> getFieldAccessors(String name) {
+        return null;
+    }
+
+    @Override
+    public SCLRelation getRelation(Name name) {
+        return null;
+    }
+
+    @Override
+    public SCLEntityType getEntityType(Name name) {
+        return null;
+    }
+
+    @Override
+    public TypeDescriptor getTypeDescriptor(TCon type) {
+        return null;
+    }
+
+    @Override
+    public EffectConstructor getEffectConstructor(TCon type) {
+        return null;
+    }
+
+    @Override
+    public TypeClass getTypeClass(TCon type) {
+        return null;
+    }
+
+    @Override
+    public Collection<TypeClassInstance> getInstances(TCon typeClass) {
+        return null;
+    }
+
+    @Override
+    public void collectRules(Collection<TransformationRule> rules) {
+    }
+}
index c5cd410f48b4aa5e0d346991dd3a0fc473a170d2..9e86a7e9d4f159ff8e230668d390b9818829687a 100644 (file)
@@ -1,5 +1,7 @@
 package org.simantics.scl.compiler.environment;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.function.Consumer;
 
 import org.simantics.scl.compiler.elaboration.chr.CHRRuleset;
@@ -23,6 +25,11 @@ public enum EmptyNamespace implements Namespace {
     public Namespace getNamespace(String name) {
         return null;
     }
+    
+    @Override
+    public Collection<String> getNamespaces() {
+        return Collections.emptyList();
+    }
 
     @Override
     public SCLValue getValue(String name) throws AmbiguousNameException {
index 3f714b497c75a82159b23e52378d352459642438..911e4b28d41d02426afe07457aeb257050437fa7 100644 (file)
@@ -9,6 +9,7 @@ import org.simantics.scl.compiler.module.repository.ModuleRepository;
 public interface EnvironmentFactory {
 
     Environment createEnvironment(CompilationContext context, ImportDeclaration[] array) throws ImportFailureException;
+    Environment createEnvironmentRobustly(CompilationContext context, ImportDeclaration[] array);
     void addBuiltinDependencies(ConcreteModule module);
     ModuleRepository getModuleRepository();
 
index 04408726092b5f5d768d3992c4878ca635f05ae3..8940b7a82d76a55a72a36daa155aea0563b1f1a4 100644 (file)
@@ -12,20 +12,20 @@ import org.simantics.scl.compiler.module.repository.UpdateListener;
 
 public class EnvironmentFactoryImpl implements EnvironmentFactory {
 
-    private final ModuleRepository environment;
+    private final ModuleRepository repository;
     private final ImportDeclaration[] builtinImports;
     private final UpdateListener listener;
     
-    public EnvironmentFactoryImpl(ModuleRepository environment,
+    public EnvironmentFactoryImpl(ModuleRepository repository,
             ImportDeclaration[] builtinImports, UpdateListener listener) {
-        this.environment = environment;
+        this.repository = repository;
         this.builtinImports = builtinImports;
         this.listener = listener;
     }
     
     @Override
     public ModuleRepository getModuleRepository() {
-        return environment;
+        return repository;
     }
 
     @Override
@@ -43,7 +43,25 @@ public class EnvironmentFactoryImpl implements EnvironmentFactory {
                 acceptedBuiltinImports.toArray(new ImportDeclaration[acceptedBuiltinImports.size()]),
                 acceptedBuiltinImports.size() + imports.length);
         System.arraycopy(imports, 0, is, acceptedBuiltinImports.size(), imports.length);
-        return environment.createEnvironment(context, is, listener);
+        return repository.createEnvironment(context, is, listener);
+    }
+    
+    @Override
+    public Environment createEnvironmentRobustly(CompilationContext context, ImportDeclaration[] imports) {
+        ArrayList<ImportDeclaration> acceptedBuiltinImports = 
+                new ArrayList<ImportDeclaration>(builtinImports.length);
+        loop: for(ImportDeclaration builtinImport : builtinImports) {
+            for(ImportDeclaration decl : imports)
+                if(decl.moduleName.equals(builtinImport.moduleName) &&
+                        decl.localName.equals(builtinImport.localName))
+                    continue loop;
+            acceptedBuiltinImports.add(builtinImport);
+        }
+        ImportDeclaration[] is = Arrays.copyOf(
+                acceptedBuiltinImports.toArray(new ImportDeclaration[acceptedBuiltinImports.size()]),
+                acceptedBuiltinImports.size() + imports.length);
+        System.arraycopy(imports, 0, is, acceptedBuiltinImports.size(), imports.length);
+        return repository.createEnvironmentRobustly(context, is, listener);
     }
     
     @Override
index e5af69204006bb768891a9665b9c0aeb54af4380..792734c5d80255ce42b6b073fdafb4dfe74db42c 100644 (file)
@@ -1,5 +1,6 @@
 package org.simantics.scl.compiler.environment;
 
+import java.util.Collection;
 import java.util.function.Consumer;
 
 import org.simantics.scl.compiler.elaboration.chr.CHRRuleset;
@@ -24,6 +25,8 @@ public interface Namespace {
         */
     Namespace getNamespace(String name);
     
+    Collection<String> getNamespaces();
+    
     /**
      * Get an SCLValue for a given name. The same instance is returned on each call.
      * @param name  the name of a defined value
index 214749bcb05b881c660325f29c2d53bd26e61553..0dd144053aa584d66c54c10175d29c1927a36e54 100644 (file)
@@ -1,6 +1,7 @@
 package org.simantics.scl.compiler.environment;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.function.Consumer;
 
 import org.simantics.scl.compiler.elaboration.chr.CHRRuleset;
@@ -45,6 +46,11 @@ public class NamespaceImpl implements Namespace {
     public Namespace getNamespace(String name) {
         return namespaceMap.get(name);
     }
+    
+    @Override
+    public Collection<String> getNamespaces() {
+        return namespaceMap.keySet();
+    }
 
     @Override
     public SCLValue getValue(String name) throws AmbiguousNameException {
index 608f405b826ed11848b156b6e229ee4250998544..785b2c44f23e28990839c7b74f7fbf63247fa1b4 100644 (file)
@@ -15,6 +15,11 @@ public class DAnnotationAst extends DeclarationAst {
         this.id = id;
         this.parameters = parameters.toArray(new Expression[parameters.size()]);
     }
+    
+    public DAnnotationAst(Token id, Expression ... parameters) {
+        this.id = id;
+        this.parameters = parameters;
+    }
 
     @Override
     public void toString(int indentation, StringBuilder b) {
index 7cf1fb754a954a3b29f13415a036f99c364d8b1f..9c1a61535531e97b55acf36d0d6830e3b0ec6313 100644 (file)
@@ -129,6 +129,10 @@ public class SCLParserImpl extends SCLParser {
         this.lexer = new SCLPostLexer(reader);
     }
     
+    public SCLPostLexer getLexer() {
+        return lexer;
+    }
+    
     public void setCompilationContext(CompilationContext context) {
         this.context = context;
         lexer.setCompilationContext(context);
index 9598f49d56860c46c32ddf4d3b5b9db41d0bf168..96d3bbc06c67a2c71465742deab22cd893912c54 100644 (file)
@@ -62,7 +62,7 @@ public class SCLPostLexer {
     int lineStart = 0;
     boolean firstTokenOfLine = true;
     private SCLParserOptions options;
-    private boolean isFirstToken = true;
+    public boolean isFirstToken = true;
     private CompilationContext context;
     
     /**
index b92f3dff6417c04b5d23b9dfe0899e7315759ed6..e52420925ea4763b332361633ad05a8565f5a44e 100644 (file)
@@ -5,6 +5,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.SynchronousQueue;
 import java.util.function.Consumer;
 
 import org.simantics.scl.compiler.common.names.Name;
@@ -249,7 +250,7 @@ public class ConcreteModule implements Module {
         this.values.forEachEntry(new TObjectObjectProcedure<String,SCLValue>() {
             @Override
             public boolean execute(String name, SCLValue value) {
-                if(value.isPrivate())
+                if(value.isPrivateOrDerived())
                     return true;
                 String lowerPrefix = prefix.toLowerCase();
                 String lowerName = name.toLowerCase();
index 7c3de572f78456db563a65011d64953a26cb546b..f2e4e97ae09944f7132c8ac6d1d03f054de41753 100644 (file)
@@ -10,6 +10,7 @@ import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
 import org.simantics.scl.compiler.compilation.CompilationContext;
 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
 import org.simantics.scl.compiler.environment.ConcreteEnvironment;
+import org.simantics.scl.compiler.environment.EmptyEnvironment;
 import org.simantics.scl.compiler.environment.Environment;
 import org.simantics.scl.compiler.environment.NamespaceImpl.ModuleImport;
 import org.simantics.scl.compiler.environment.NamespaceSpec;
@@ -162,7 +163,7 @@ public class ModuleRepository {
                         Collection<ImportDeclaration> dependencies = module.getDependencies();
                         THashMap<String, ModuleEntry> moduleEntries;
                         try {
-                            moduleEntries = getModuleEntries(null, dependencies.toArray(new ImportDeclaration[dependencies.size()]), null);
+                            moduleEntries = getModuleEntries(null, dependencies.toArray(new ImportDeclaration[dependencies.size()]), null, false);
                         } catch (ImportFailureException e) {
                             throw new InternalCompilerError(e);
                         }
@@ -259,7 +260,8 @@ public class ModuleRepository {
     private THashMap<String, ModuleEntry> getModuleEntries(
             CompilationContext compilationContext,
             ImportDeclaration[] imports,
-            UpdateListener listener) throws ImportFailureException {
+            UpdateListener listener,
+            boolean robustly) throws ImportFailureException {
         THashMap<String, ModuleEntry> result = new THashMap<String, ModuleEntry>();
         Collection<ImportFailure> failures = null;
         
@@ -297,7 +299,7 @@ public class ModuleRepository {
             }
         }
         
-        if(failures != null)
+        if(failures != null && !robustly)
             throw new ImportFailureException(failures);
         
         return result;
@@ -337,11 +339,25 @@ public class ModuleRepository {
             CompilationContext compilationContext,
             ImportDeclaration[] imports,
             UpdateListener listener) throws ImportFailureException {
-        THashMap<String, ModuleEntry> entries = getModuleEntries(compilationContext, imports, listener);
+        THashMap<String, ModuleEntry> entries = getModuleEntries(compilationContext, imports, listener, false);
         THashMap<String, Module> moduleMap = mapEntriesToModules(entries);
         return createEnvironment(moduleMap, imports);
     }
     
+    public Environment createEnvironmentRobustly(
+            CompilationContext compilationContext,
+            ImportDeclaration[] imports,
+            UpdateListener listener) {
+        try {
+            THashMap<String, ModuleEntry> entries = getModuleEntries(compilationContext, imports, listener, true);
+            THashMap<String, Module> moduleMap = mapEntriesToModules(entries);
+            return createEnvironment(moduleMap, imports);
+        } catch(ImportFailureException e) {
+            // Should not happen because of robust flag
+            return EmptyEnvironment.INSTANCE;
+        }
+    }
+    
     public Environment createEnvironment(
             EnvironmentSpecification specification,
             UpdateListener listener) throws ImportFailureException {
@@ -367,7 +383,7 @@ public class ModuleRepository {
             ImportDeclaration[] imports,
             ClassLoader parentClassLoader,
             UpdateListener listener) throws ImportFailureException {
-        THashMap<String, ModuleEntry> entries = getModuleEntries(null, imports, listener);
+        THashMap<String, ModuleEntry> entries = getModuleEntries(null, imports, listener, false);
         THashMap<String, Module> moduleMap = mapEntriesToModules(entries);
         Environment environment = createEnvironment(moduleMap, imports);
         THashMap<String, RuntimeModule> runtimeModuleMap = mapEntriesToRuntimeModules(entries);
index 1316ee5d7075afcb65c05ec9aad3eaf5b1d0d2f1..fce9fbf4f4234bd8a8a4a645b5ff0c992a3562dd 100644 (file)
@@ -34,7 +34,7 @@ public class ClassModuleSource extends EncodedTextualModuleSource {
     }
     
     @Override
-    protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
+    public ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
         return builtinImports;
     }
 }
index 5bdb533ba5603a6cdb81f389d47c3db47d9abe0a..19f50a6a9817338614fb6c0675a49f184316eaf7 100644 (file)
@@ -36,7 +36,7 @@ public class FileModuleSource extends EncodedTextualModuleSource {
     }
     
     @Override
-    protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
+    public ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
         return builtinImports;
     }
 }
index 50cbb1ac461ea271e0c4700d37d149fc23a351a2..e0d1c91e4da51e3b6b0b8d583b848857ae9a11b6 100644 (file)
@@ -69,7 +69,7 @@ public abstract class TextualModuleSource implements ModuleSource {
         }
     }
     
-    protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
+    public ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
         return DEFAULT_IMPORTS;
     }
     
index 92385a1ed3f9be600ffb0ea33d0ff5e10cda913b..af6af0872163a8e3ec6be21461becc5c90199c68 100644 (file)
@@ -54,7 +54,7 @@ public class BundleModuleSource extends EncodedTextualModuleSource implements Up
     }
 
     @Override
-    protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
+    public ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
         if(bundle.getSymbolicName().equals("org.simantics.scl.runtime"))
             return DEFAULT_IMPORTS_FOR_STANDARD_LIBRARY;
         else
index a662791dd58dfa27c1ccf095fb628f59cd5ac05d..ed7b7250ddb12c70792d9f8e24d186527bfb060e 100644 (file)
@@ -95,7 +95,7 @@ public class TestBase {
             moduleSources[i] = new StringModuleSource(
                     moduleNames[i], getClass().getClassLoader(), moduleTexts[i]) {
                 @Override
-                protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
+                public ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
                     return ImportDeclaration.ONLY_BUILTINS;
                 }
             };
index 9d501c16b28af9d917c0a96282a9a651edd7a348..5d6d166b2ffa3a4c701d660c11a0ab15a83b7c23 100644 (file)
@@ -22,7 +22,7 @@ public class TestClassNaming {
         }
         
         @Override
-        protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
+        public ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
             return new ImportDeclaration[] {new ImportDeclaration("Builtin", "")};
         }
     }
diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/SplittingExample1.scl b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/SplittingExample1.scl
new file mode 100644 (file)
index 0000000..27964ba
--- /dev/null
@@ -0,0 +1,22 @@
+ASD (
+a
+b
+)
+
+/*
+a
+b
+*/
+
+"""
+a
+b
+"""
+
+"ab"// (
+
+a "
+
+b /*
+
+c
\ No newline at end of file
diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/TestPrefixUtil.java b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/TestPrefixUtil.java
new file mode 100644 (file)
index 0000000..e61d22a
--- /dev/null
@@ -0,0 +1,22 @@
+package org.simantics.scl.compiler.tests.completions;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.simantics.scl.compiler.completions.PrefixUtil;
+
+
+public class TestPrefixUtil {
+    @Test
+    public void testFindPrefix() {
+        Assert.assertEquals("aaa", PrefixUtil.findPrefix("aaa aaab bbb", 7));
+        Assert.assertEquals("aaa.aaa", PrefixUtil.findPrefix("aaa.aaab bbb", 7));
+        Assert.assertEquals("a3a.a3a", PrefixUtil.findPrefix("a3a.a3ab bbb", 7));
+        Assert.assertEquals("aaa", PrefixUtil.findPrefix("aa .aaab bbb", 7));
+    }
+    
+    @Test
+    public void testSplitPrefix() {
+        Assert.assertArrayEquals(new String[] {"aa", "bb", "cc"}, PrefixUtil.splitPrefix("aa.bb.cc"));
+        Assert.assertArrayEquals(new String[] {"aa", "bb", ""}, PrefixUtil.splitPrefix("aa.bb."));
+    }
+}
diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/TestRobustModuleSplitter.java b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/TestRobustModuleSplitter.java
new file mode 100644 (file)
index 0000000..a795754
--- /dev/null
@@ -0,0 +1,34 @@
+package org.simantics.scl.compiler.tests.completions;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import org.junit.Test;
+import org.simantics.scl.compiler.completions.parsing.ModuleSegment;
+import org.simantics.scl.compiler.completions.parsing.RobustModuleSplitter;
+
+public class TestRobustModuleSplitter {
+    private String readSource(String name) throws IOException {
+        InputStreamReader reader = new InputStreamReader(
+                TestRobustModuleSplitter.class.getResourceAsStream(name),
+                Charset.forName("UTF-8"));
+        StringBuilder b = new StringBuilder();
+        while(true) {
+            int c = reader.read();
+            if(c < 0)
+                break;
+            b.append((char)c);
+        }
+        return b.toString();
+    }
+    
+    @Test
+    public void example1() throws IOException {
+        String sourceText = readSource("SplittingExample1.scl");
+        for(ModuleSegment segment : RobustModuleSplitter.split(sourceText)) {
+            System.out.println("---- ("+segment.parenthesesBalance+") ----------------------------------------------");
+            System.out.println(sourceText.substring(segment.begin, segment.end).trim());
+        }
+    }
+}