-package org.simantics.scl.ui.editor.completion;\r
-\r
-import java.io.StringReader;\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-import java.util.List;\r
-\r
-import org.eclipse.jface.text.IDocument;\r
-import org.eclipse.jface.text.contentassist.ICompletionProposal;\r
-import org.simantics.scl.compiler.common.names.Name;\r
-import org.simantics.scl.compiler.compilation.EnvironmentOfModule;\r
-import org.simantics.scl.compiler.elaboration.modules.SCLValue;\r
-import org.simantics.scl.compiler.environment.AmbiguousNameException;\r
-import org.simantics.scl.compiler.environment.Environment;\r
-import org.simantics.scl.compiler.environment.Environments;\r
-import org.simantics.scl.compiler.errors.Failable;\r
-import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException;\r
-import org.simantics.scl.compiler.internal.parsing.parser.SCLParserImpl;\r
-import org.simantics.scl.compiler.module.ImportDeclaration;\r
-import org.simantics.scl.compiler.module.Module;\r
-import org.simantics.scl.compiler.module.repository.ImportFailureException;\r
-import org.simantics.scl.compiler.source.TextualModuleSource;\r
-import org.simantics.scl.compiler.types.TCon;\r
-import org.simantics.scl.osgi.SCLOsgi;\r
-\r
-public class SCLTextEditorEnvironment {\r
-\r
- private String moduleName;\r
- private SCLCompletionProposal[] proposalCache = new SCLCompletionProposal[0];\r
- private List<SCLCompletionProposal> moduleProposalCache = new ArrayList<>(0);\r
- private Environment env;\r
- \r
- private List<ImportDeclaration> cachedImports = new ArrayList<>();\r
- private boolean cacheUpdated = false;\r
- \r
- public SCLTextEditorEnvironment(String moduleName) {\r
- this.moduleName = moduleName;\r
- }\r
- \r
- public void updateModuleName(String moduleName) {\r
- this.moduleName = moduleName;\r
- }\r
- \r
- public void updateEnvironment(IDocument document) {\r
- String contents = document.get();\r
- String[] lines = contents.split("\\R+");\r
- List<ImportDeclaration> imports = new ArrayList<>();\r
- imports.addAll(Arrays.asList(TextualModuleSource.DEFAULT_IMPORTS));\r
- for (String line : lines) {\r
- line = line.trim();\r
- if (line.startsWith("import") || line.startsWith("include")) {\r
- SCLParserImpl parser = new SCLParserImpl(new StringReader(line));\r
- try {\r
- ImportDeclaration importDecl = (ImportDeclaration)parser.parseImport();\r
- imports.add(importDecl);\r
- } catch (SCLSyntaxErrorException e) {\r
- // Import cannot be handled, ignore\r
- }\r
- }\r
- }\r
-\r
- imports = processRelativeImports(imports);\r
- if (!imports.equals(cachedImports)) {\r
- cachedImports = imports;\r
- try {\r
- env = SCLOsgi.MODULE_REPOSITORY.createEnvironment(cachedImports.toArray(new ImportDeclaration[cachedImports.size()]), null);\r
- Failable<Module> module = SCLOsgi.MODULE_REPOSITORY.getModule(moduleName);\r
- if(module.didSucceed())\r
- env = new EnvironmentOfModule(env, module.getResult()); \r
- } catch (ImportFailureException e) {\r
- //e.printStackTrace();\r
- }\r
- }\r
- }\r
- \r
- public ICompletionProposal[] getCompletionProposals(String prefix, int offset) {\r
- int p = prefix.lastIndexOf('.');\r
- String lastPart = p==-1 ? prefix : prefix.substring(p+1);\r
- \r
- List<SCLCompletionProposal> proposals = new ArrayList<>();\r
- for(SCLValue value : Environments.findValuesForPrefix(env, prefix)) {\r
- Name name = value.getName();\r
- if((name.module.equals(moduleName) || !value.isPrivate()) && !(name.name.contains("$") && Character.isLetter(name.name.charAt(0))))\r
- proposals.add(new SCLCompletionProposal(value, offset - lastPart.length(), lastPart));\r
- }\r
- for(TCon type : Environments.findTypesForPrefix(env, prefix)) {\r
- proposals.add(new SCLCompletionProposal(type.name, type.module, SCLCompletionType.TYPE, offset - lastPart.length(), lastPart));\r
- }\r
- \r
- if(!prefix.contains(".")) {\r
- for (ImportDeclaration decl : cachedImports) {\r
- if (decl.localName != null && !decl.localName.isEmpty() && decl.localName.toLowerCase().startsWith(prefix.toLowerCase())) {\r
- proposals.add(new SCLCompletionProposal(decl.localName, decl.moduleName, SCLCompletionType.CONST, offset - prefix.length(), prefix));\r
- }\r
- }\r
- }\r
- Collections.sort(proposals, COMPARATOR);\r
- moduleProposalCache = proposals;\r
- proposalCache = moduleProposalCache.toArray(new SCLCompletionProposal[moduleProposalCache.size()]);\r
- return proposalCache;\r
- }\r
- \r
- private ArrayList<ImportDeclaration> processRelativeImports(List<ImportDeclaration> relativeImports) {\r
- ArrayList<ImportDeclaration> absoluteImports = new ArrayList<ImportDeclaration>(relativeImports.size());\r
- for(ImportDeclaration relativeImport : relativeImports) {\r
- if(relativeImport.moduleName.startsWith(".")) {\r
- String absoluteModuleName = convertRelativeModulePath(relativeImport.moduleName);\r
- if(absoluteModuleName != null)\r
- absoluteImports.add(new ImportDeclaration(\r
- absoluteModuleName, relativeImport.localName,\r
- relativeImport.reexport, relativeImport.spec));\r
- }\r
- else\r
- absoluteImports.add(relativeImport);\r
- }\r
- return absoluteImports;\r
- }\r
-\r
- private String convertRelativeModulePath(String relativeModuleName) {\r
- String originalRelativeModuleName = relativeModuleName;\r
- int p = moduleName.lastIndexOf('/');\r
- String parentPackage = p < 0 ? "" : moduleName.substring(0, p);\r
- while(relativeModuleName.startsWith(".")) {\r
- if(relativeModuleName.startsWith("./")) {\r
- relativeModuleName = relativeModuleName.substring(2);\r
- }\r
- else if(relativeModuleName.startsWith("../")) {\r
- relativeModuleName = relativeModuleName.substring(3);\r
- if(parentPackage.isEmpty()) {\r
- System.err.println("Couldn't resolve the relative module name " + originalRelativeModuleName + " when the current module name is " + moduleName + ".");\r
- return null;\r
- }\r
- p = parentPackage.lastIndexOf('/');\r
- parentPackage = p < 0 ? "" : parentPackage.substring(0, p);\r
- }\r
- else {\r
- System.err.println("Couldn't resolve the relative module name " + originalRelativeModuleName + ". It has an invalid syntax.");\r
- return null;\r
- }\r
- }\r
- return parentPackage + "/" + relativeModuleName;\r
- }\r
-\r
- private static final Comparator<SCLCompletionProposal> COMPARATOR = new Comparator<SCLCompletionProposal>() {\r
-\r
- @Override\r
- public int compare(SCLCompletionProposal prop1, SCLCompletionProposal prop2) {\r
- if (prop1.isPrivate() && !prop2.isPrivate())\r
- return -1;\r
- else if (!prop1.isPrivate() && prop2.isPrivate())\r
- return 1;\r
- return prop1.getName().compareTo(prop2.getName());\r
- }\r
- };\r
-\r
- public SCLValue getValue(String text) {\r
- try {\r
- return Environments.getValue(env, text);\r
- } catch (AmbiguousNameException e) {\r
- // TODO could also return one of the conflicting alternatives\r
- return null;\r
- }\r
- }\r
- \r
- public String getHoverInfo(String text) {\r
- SCLValue value = getValue(text);\r
- if (value != null)\r
- return value.getDocumentation();\r
- else\r
- return null;\r
- }\r
-}\r
+package org.simantics.scl.ui.editor.completion;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.simantics.scl.compiler.common.names.Name;
+import org.simantics.scl.compiler.compilation.EnvironmentOfModule;
+import org.simantics.scl.compiler.elaboration.modules.SCLValue;
+import org.simantics.scl.compiler.environment.AmbiguousNameException;
+import org.simantics.scl.compiler.environment.Environment;
+import org.simantics.scl.compiler.environment.Environments;
+import org.simantics.scl.compiler.errors.Failable;
+import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException;
+import org.simantics.scl.compiler.internal.parsing.parser.SCLParserImpl;
+import org.simantics.scl.compiler.module.ImportDeclaration;
+import org.simantics.scl.compiler.module.InvalidModulePathException;
+import org.simantics.scl.compiler.module.Module;
+import org.simantics.scl.compiler.module.ModuleUtils;
+import org.simantics.scl.compiler.module.repository.ImportFailureException;
+import org.simantics.scl.compiler.types.TCon;
+import org.simantics.scl.osgi.SCLOsgi;
+
+public class SCLTextEditorEnvironment {
+
+ private String moduleName;
+ private SCLCompletionProposal[] proposalCache = new SCLCompletionProposal[0];
+ private List<SCLCompletionProposal> moduleProposalCache = new ArrayList<>(0);
+ private Environment env;
+
+ private List<ImportDeclaration> cachedImports = new ArrayList<>();
+ private boolean cacheUpdated = false;
+
+ public SCLTextEditorEnvironment(String moduleName) {
+ this.moduleName = moduleName;
+ }
+
+ public void updateModuleName(String moduleName) {
+ this.moduleName = moduleName;
+ }
+
+ public void updateEnvironment(IDocument document) {
+ String contents = document.get();
+ String[] lines = contents.split("\\R+");
+ List<ImportDeclaration> imports = new ArrayList<>();
+ imports.add(new ImportDeclaration("StandardLibrary", ""));
+ for (String line : lines) {
+ line = line.trim();
+ if (line.startsWith("import") || line.startsWith("include")) {
+ SCLParserImpl parser = new SCLParserImpl(new StringReader(line));
+ try {
+ ImportDeclaration importDecl = (ImportDeclaration)parser.parseImport();
+ imports.add(importDecl);
+ } catch (SCLSyntaxErrorException e) {
+ // Import cannot be handled, ignore
+ }
+ }
+ }
+
+ imports = processRelativeImports(imports);
+ if (!imports.equals(cachedImports)) {
+ cachedImports = imports;
+ try {
+ env = SCLOsgi.MODULE_REPOSITORY.createEnvironment(cachedImports.toArray(new ImportDeclaration[cachedImports.size()]), null);
+ Failable<Module> module = SCLOsgi.MODULE_REPOSITORY.getModule(moduleName);
+ if(module.didSucceed())
+ env = new EnvironmentOfModule(env, module.getResult());
+ } catch (ImportFailureException e) {
+ //e.printStackTrace();
+ }
+ }
+ }
+
+ public ICompletionProposal[] getCompletionProposals(String prefix, int offset) {
+ int p = prefix.lastIndexOf('.');
+ String lastPart = p==-1 ? prefix : prefix.substring(p+1);
+
+ List<SCLCompletionProposal> proposals = new ArrayList<>();
+ for(SCLValue value : Environments.findValuesForPrefix(env, prefix)) {
+ Name name = value.getName();
+ if((name.module.equals(moduleName) || !value.isPrivate()) && !(name.name.contains("$") && Character.isLetter(name.name.charAt(0))))
+ proposals.add(new SCLCompletionProposal(value, offset - lastPart.length(), lastPart));
+ }
+ for(TCon type : Environments.findTypesForPrefix(env, prefix)) {
+ proposals.add(new SCLCompletionProposal(type.name, type.module, SCLCompletionType.TYPE, offset - lastPart.length(), lastPart));
+ }
+
+ if(!prefix.contains(".")) {
+ for (ImportDeclaration decl : cachedImports) {
+ if (decl.localName != null && !decl.localName.isEmpty() && decl.localName.toLowerCase().startsWith(prefix.toLowerCase())) {
+ proposals.add(new SCLCompletionProposal(decl.localName, decl.moduleName, SCLCompletionType.CONST, offset - prefix.length(), prefix));
+ }
+ }
+ }
+ Collections.sort(proposals, COMPARATOR);
+ moduleProposalCache = proposals;
+ proposalCache = moduleProposalCache.toArray(new SCLCompletionProposal[moduleProposalCache.size()]);
+ return proposalCache;
+ }
+
+ private ArrayList<ImportDeclaration> processRelativeImports(List<ImportDeclaration> relativeImports) {
+ ArrayList<ImportDeclaration> absoluteImports = new ArrayList<ImportDeclaration>(relativeImports.size());
+ for(ImportDeclaration relativeImport : relativeImports) {
+ try {
+ String absoluteModuleName = ModuleUtils.resolveAbsolutePath(moduleName, relativeImport.moduleName);
+ absoluteImports.add(new ImportDeclaration(
+ relativeImport.location,
+ absoluteModuleName, relativeImport.localName,
+ relativeImport.reexport, relativeImport.spec));
+ } catch (InvalidModulePathException e) {
+ // Nothing to do
+ }
+ }
+ return absoluteImports;
+ }
+
+ private static final Comparator<SCLCompletionProposal> COMPARATOR = new Comparator<SCLCompletionProposal>() {
+
+ @Override
+ public int compare(SCLCompletionProposal prop1, SCLCompletionProposal prop2) {
+ if (prop1.isPrivate() && !prop2.isPrivate())
+ return -1;
+ else if (!prop1.isPrivate() && prop2.isPrivate())
+ return 1;
+ return prop1.getName().compareTo(prop2.getName());
+ }
+ };
+
+ public SCLValue getValue(String text) {
+ try {
+ return Environments.getValue(env, text);
+ } catch (AmbiguousNameException e) {
+ // TODO could also return one of the conflicting alternatives
+ return null;
+ }
+ }
+
+ public String getHoverInfo(String text) {
+ SCLValue value = getValue(text);
+ if (value != null)
+ return value.getDocumentation();
+ else
+ return null;
+ }
+}