]> gerrit.simantics Code Review - simantics/platform.git/blob
9006f3cc2bd12d4d2d3dc98d654940dd209533cb
[simantics/platform.git] /
1 package org.simantics.scl.ui.editor.completion;
2
3 import java.io.StringReader;
4 import java.util.ArrayList;
5 import java.util.Collections;
6 import java.util.Comparator;
7 import java.util.List;
8
9 import org.eclipse.jface.text.IDocument;
10 import org.eclipse.jface.text.contentassist.ICompletionProposal;
11 import org.simantics.scl.compiler.common.names.Name;
12 import org.simantics.scl.compiler.compilation.EnvironmentOfModule;
13 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
14 import org.simantics.scl.compiler.environment.AmbiguousNameException;
15 import org.simantics.scl.compiler.environment.Environment;
16 import org.simantics.scl.compiler.environment.Environments;
17 import org.simantics.scl.compiler.errors.Failable;
18 import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException;
19 import org.simantics.scl.compiler.internal.parsing.parser.SCLParserImpl;
20 import org.simantics.scl.compiler.module.ImportDeclaration;
21 import org.simantics.scl.compiler.module.Module;
22 import org.simantics.scl.compiler.module.repository.ImportFailureException;
23 import org.simantics.scl.compiler.types.TCon;
24 import org.simantics.scl.osgi.SCLOsgi;
25
26 public class SCLTextEditorEnvironment {
27
28     private String moduleName;
29     private SCLCompletionProposal[] proposalCache = new SCLCompletionProposal[0];
30     private List<SCLCompletionProposal> moduleProposalCache = new ArrayList<>(0);
31     private Environment env;
32     
33     private List<ImportDeclaration> cachedImports = new ArrayList<>();
34     private boolean cacheUpdated = false;
35     
36     public SCLTextEditorEnvironment(String moduleName) {
37         this.moduleName = moduleName;
38     }
39     
40     public void updateModuleName(String moduleName) {
41         this.moduleName = moduleName;
42     }
43     
44     public void updateEnvironment(IDocument document) {
45         String contents = document.get();
46         String[] lines = contents.split("\\R+");
47         List<ImportDeclaration> imports = new ArrayList<>();
48         imports.add(new ImportDeclaration("StandardLibrary", ""));
49         for (String line : lines) {
50             line = line.trim();
51             if (line.startsWith("import") || line.startsWith("include")) {
52                 SCLParserImpl parser = new SCLParserImpl(new StringReader(line));
53                 try {
54                     ImportDeclaration importDecl = (ImportDeclaration)parser.parseImport();
55                     imports.add(importDecl);
56                 } catch (SCLSyntaxErrorException e) {
57                     // Import cannot be handled, ignore
58                 }
59             }
60         }
61
62         imports = processRelativeImports(imports);
63         if (!imports.equals(cachedImports)) {
64             cachedImports = imports;
65             try {
66                 env = SCLOsgi.MODULE_REPOSITORY.createEnvironment(cachedImports.toArray(new ImportDeclaration[cachedImports.size()]), null);
67                 Failable<Module> module = SCLOsgi.MODULE_REPOSITORY.getModule(moduleName);
68                 if(module.didSucceed())
69                     env = new EnvironmentOfModule(env, module.getResult()); 
70             } catch (ImportFailureException e) {
71                 //e.printStackTrace();
72             }
73         }
74     }
75     
76     public ICompletionProposal[] getCompletionProposals(String prefix, int offset) {
77         int p = prefix.lastIndexOf('.');
78         String lastPart = p==-1 ? prefix : prefix.substring(p+1);
79         
80         List<SCLCompletionProposal> proposals = new ArrayList<>();
81         for(SCLValue value : Environments.findValuesForPrefix(env, prefix)) {
82             Name name = value.getName();
83             if((name.module.equals(moduleName) || !value.isPrivate()) && !(name.name.contains("$") && Character.isLetter(name.name.charAt(0))))
84                 proposals.add(new SCLCompletionProposal(value, offset - lastPart.length(), lastPart));
85         }
86         for(TCon type : Environments.findTypesForPrefix(env, prefix)) {
87             proposals.add(new SCLCompletionProposal(type.name, type.module, SCLCompletionType.TYPE, offset - lastPart.length(), lastPart));
88         }
89         
90         if(!prefix.contains(".")) {
91             for (ImportDeclaration decl : cachedImports) {
92                 if (decl.localName != null && !decl.localName.isEmpty() && decl.localName.toLowerCase().startsWith(prefix.toLowerCase())) {
93                     proposals.add(new SCLCompletionProposal(decl.localName, decl.moduleName, SCLCompletionType.CONST, offset - prefix.length(), prefix));
94                 }
95             }
96         }
97         Collections.sort(proposals, COMPARATOR);
98         moduleProposalCache = proposals;
99         proposalCache = moduleProposalCache.toArray(new SCLCompletionProposal[moduleProposalCache.size()]);
100         return proposalCache;
101     }
102     
103     private ArrayList<ImportDeclaration> processRelativeImports(List<ImportDeclaration> relativeImports) {
104         ArrayList<ImportDeclaration> absoluteImports = new ArrayList<ImportDeclaration>(relativeImports.size());
105         for(ImportDeclaration relativeImport : relativeImports) {
106             if(relativeImport.moduleName.startsWith(".")) {
107                 String absoluteModuleName = convertRelativeModulePath(relativeImport.moduleName);
108                 if(absoluteModuleName != null)
109                     absoluteImports.add(new ImportDeclaration(
110                             absoluteModuleName, relativeImport.localName,
111                             relativeImport.reexport, relativeImport.spec));
112             }
113             else
114                 absoluteImports.add(relativeImport);
115         }
116         return absoluteImports;
117     }
118
119     private String convertRelativeModulePath(String relativeModuleName) {
120         String originalRelativeModuleName = relativeModuleName;
121         int p = moduleName.lastIndexOf('/');
122         String parentPackage = p < 0 ? "" : moduleName.substring(0, p);
123         while(relativeModuleName.startsWith(".")) {
124             if(relativeModuleName.startsWith("./")) {
125                 relativeModuleName = relativeModuleName.substring(2);
126             }
127             else if(relativeModuleName.startsWith("../")) {
128                 relativeModuleName = relativeModuleName.substring(3);
129                 if(parentPackage.isEmpty()) {
130                     System.err.println("Couldn't resolve the relative module name " + originalRelativeModuleName + " when the current module name is " + moduleName + ".");
131                     return null;
132                 }
133                 p = parentPackage.lastIndexOf('/');
134                 parentPackage = p < 0 ? "" : parentPackage.substring(0, p);
135             }
136             else {
137                 System.err.println("Couldn't resolve the relative module name " + originalRelativeModuleName + ". It has an invalid syntax.");
138                 return null;
139             }
140         }
141         return parentPackage + "/" + relativeModuleName;
142     }
143
144     private static final Comparator<SCLCompletionProposal> COMPARATOR = new Comparator<SCLCompletionProposal>() {
145
146         @Override
147         public int compare(SCLCompletionProposal prop1, SCLCompletionProposal prop2) {
148             if (prop1.isPrivate() && !prop2.isPrivate())
149                 return -1;
150             else if (!prop1.isPrivate() && prop2.isPrivate())
151                 return 1;
152             return prop1.getName().compareTo(prop2.getName());
153         }
154     };
155
156     public SCLValue getValue(String text) {
157         try {
158             return Environments.getValue(env, text);
159         } catch (AmbiguousNameException e) {
160             // TODO could also return one of the conflicting alternatives
161             return null;
162         }
163     }
164     
165     public String getHoverInfo(String text) {
166         SCLValue value = getValue(text);
167         if (value != null)
168             return value.getDocumentation();
169         else
170             return null;
171     }
172 }