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