]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/editor/completion/SCLTextEditorEnvironment.java
98b51917744aaa43d7f179a0712e72b23bfdb29a
[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;
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.InvalidModulePathException;
22 import org.simantics.scl.compiler.module.Module;
23 import org.simantics.scl.compiler.module.ModuleUtils;
24 import org.simantics.scl.compiler.module.repository.ImportFailureException;
25 import org.simantics.scl.compiler.types.TCon;
26 import org.simantics.scl.osgi.SCLOsgi;
27
28 public class SCLTextEditorEnvironment {
29
30     private String moduleName;
31     private SCLCompletionProposal[] proposalCache = new SCLCompletionProposal[0];
32     private List<SCLCompletionProposal> moduleProposalCache = new ArrayList<>(0);
33     private Environment env;
34     
35     private List<ImportDeclaration> cachedImports = new ArrayList<>();
36     private boolean cacheUpdated = false;
37     
38     public SCLTextEditorEnvironment(String moduleName) {
39         this.moduleName = moduleName;
40     }
41     
42     public void updateModuleName(String moduleName) {
43         this.moduleName = moduleName;
44     }
45     
46     public void updateEnvironment(IDocument document) {
47         String contents = document.get();
48         String[] lines = contents.split("\\R+");
49         List<ImportDeclaration> imports = new ArrayList<>();
50         imports.add(new ImportDeclaration("StandardLibrary", ""));
51         for (String line : lines) {
52             line = line.trim();
53             if (line.startsWith("import") || line.startsWith("include")) {
54                 SCLParserImpl parser = new SCLParserImpl(new StringReader(line));
55                 try {
56                     ImportDeclaration importDecl = (ImportDeclaration)parser.parseImport();
57                     imports.add(importDecl);
58                 } catch (SCLSyntaxErrorException e) {
59                     // Import cannot be handled, ignore
60                 }
61             }
62         }
63
64         imports = processRelativeImports(imports);
65         if (!imports.equals(cachedImports)) {
66             cachedImports = imports;
67             try {
68                 env = SCLOsgi.MODULE_REPOSITORY.createEnvironment(cachedImports.toArray(new ImportDeclaration[cachedImports.size()]), null);
69                 Failable<Module> module = SCLOsgi.MODULE_REPOSITORY.getModule(moduleName);
70                 if(module.didSucceed())
71                     env = new EnvironmentOfModule(env, module.getResult()); 
72             } catch (ImportFailureException e) {
73                 //e.printStackTrace();
74             }
75         }
76     }
77     
78     public ICompletionProposal[] getCompletionProposals(String prefix, int offset) {
79         int p = prefix.lastIndexOf('.');
80         String lastPart = p==-1 ? prefix : prefix.substring(p+1);
81         
82         List<SCLCompletionProposal> proposals = new ArrayList<>();
83         for(SCLValue value : Environments.findValuesForPrefix(env, prefix)) {
84             Name name = value.getName();
85             if((name.module.equals(moduleName) || !value.isPrivate()) && !(name.name.contains("$") && Character.isLetter(name.name.charAt(0))))
86                 proposals.add(new SCLCompletionProposal(value, offset - lastPart.length(), lastPart));
87         }
88         for(TCon type : Environments.findTypesForPrefix(env, prefix)) {
89             proposals.add(new SCLCompletionProposal(type.name, type.module, SCLCompletionType.TYPE, offset - lastPart.length(), lastPart));
90         }
91         
92         if(!prefix.contains(".")) {
93             for (ImportDeclaration decl : cachedImports) {
94                 if (decl.localName != null && !decl.localName.isEmpty() && decl.localName.toLowerCase().startsWith(prefix.toLowerCase())) {
95                     proposals.add(new SCLCompletionProposal(decl.localName, decl.moduleName, SCLCompletionType.CONST, offset - prefix.length(), prefix));
96                 }
97             }
98         }
99         Collections.sort(proposals, COMPARATOR);
100         moduleProposalCache = proposals;
101         proposalCache = moduleProposalCache.toArray(new SCLCompletionProposal[moduleProposalCache.size()]);
102         return proposalCache;
103     }
104     
105     private ArrayList<ImportDeclaration> processRelativeImports(List<ImportDeclaration> relativeImports) {
106         ArrayList<ImportDeclaration> absoluteImports = new ArrayList<ImportDeclaration>(relativeImports.size());
107         for(ImportDeclaration relativeImport : relativeImports) {
108                         try {
109                                 String absoluteModuleName = ModuleUtils.resolveAbsolutePath(moduleName, relativeImport.moduleName);
110                 absoluteImports.add(new ImportDeclaration(
111                         relativeImport.location,
112                         absoluteModuleName, relativeImport.localName,
113                         relativeImport.reexport, relativeImport.spec));
114                         } catch (InvalidModulePathException e) {
115                 // Nothing to do
116                         }
117         }
118         return absoluteImports;
119     }
120
121     private static final Comparator<SCLCompletionProposal> COMPARATOR = new Comparator<SCLCompletionProposal>() {
122
123         @Override
124         public int compare(SCLCompletionProposal prop1, SCLCompletionProposal prop2) {
125             if (prop1.isPrivate() && !prop2.isPrivate())
126                 return -1;
127             else if (!prop1.isPrivate() && prop2.isPrivate())
128                 return 1;
129             return prop1.getName().compareTo(prop2.getName());
130         }
131     };
132
133     public SCLValue getValue(String text) {
134         try {
135             return Environments.getValue(env, text);
136         } catch (AmbiguousNameException e) {
137             // TODO could also return one of the conflicting alternatives
138             return null;
139         }
140     }
141     
142     public String getHoverInfo(String text) {
143         SCLValue value = getValue(text);
144         if (value != null)
145             return value.getDocumentation();
146         else
147             return null;
148     }
149 }