package com.redhat.ceylon.eclipse.code.complete; import static com.redhat.ceylon.eclipse.code.complete.EclipseCompletionProcessor.NO_COMPLETIONS; import static com.redhat.ceylon.eclipse.code.complete.CompletionUtil.fullPath; import static com.redhat.ceylon.eclipse.code.hover.DocumentationHover.getDocumentationFor; import static com.redhat.ceylon.eclipse.code.hover.DocumentationHover.getDocumentationForModule; import static com.redhat.ceylon.eclipse.code.preferences.CeylonPreferenceInitializer.LINKED_MODE_ARGUMENTS; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getPackageName; import static com.redhat.ceylon.eclipse.ui.CeylonPlugin.getPreferences; import static com.redhat.ceylon.eclipse.ui.CeylonResources.MODULE; import static com.redhat.ceylon.eclipse.util.EditorUtil.getCurrentEditor; import static com.redhat.ceylon.eclipse.util.ModuleQueries.getModuleQuery; import static com.redhat.ceylon.eclipse.util.Nodes.getImportedName; import java.util.ArrayList; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.text.link.ILinkedModeListener; import org.eclipse.jface.text.link.LinkedModeModel; import org.eclipse.jface.text.link.ProposalPosition; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import com.redhat.ceylon.cmr.api.ModuleQuery; import com.redhat.ceylon.cmr.api.ModuleSearchResult; import com.redhat.ceylon.cmr.api.ModuleSearchResult.ModuleDetails; import com.redhat.ceylon.cmr.api.ModuleVersionDetails; import com.redhat.ceylon.common.Versions; import com.redhat.ceylon.compiler.typechecker.TypeChecker; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.eclipse.code.editor.CeylonEditor; import com.redhat.ceylon.eclipse.code.parse.CeylonParseController; import com.redhat.ceylon.eclipse.ui.CeylonResources; import com.redhat.ceylon.eclipse.util.LinkedMode; import com.redhat.ceylon.model.cmr.JDKUtils; import com.redhat.ceylon.model.typechecker.model.Module; import com.redhat.ceylon.model.typechecker.model.Scope; import com.redhat.ceylon.model.typechecker.model.Unit; public class ModuleCompletions { public static final class ModuleDescriptorProposal extends CompletionProposal { int selectionStart = -1; int selectionLength = -1; public ModuleDescriptorProposal(int offset, String prefix, String desc, String text, int selectionStart, int selectionLength) { super(offset, prefix, new FixedImageRetriever(MODULE), desc, text); this.selectionStart = selectionStart; this.selectionLength = selectionLength; } @Deprecated ModuleDescriptorProposal(int offset, String prefix, String moduleName) { super(offset, prefix, new FixedImageRetriever(MODULE), "module " + moduleName, "module " + moduleName + " \"1.0.0\" {}"); } @Override public Point getSelection(IDocument document) { if (selectionStart > 0 && selectionLength > 0) { return new Point(selectionStart, selectionLength); } else { return new Point(offset - prefix.length() + text.indexOf('\"')+1, 5); } } @Override protected boolean qualifiedNameIsPath() { return true; } } public static final class ModuleProposal extends CompletionProposal { private final int len; private final String versioned; private final ModuleDetails module; private final boolean withBody; private final ModuleVersionDetails version; private final String name; private Node node; public ModuleProposal(int offset, String prefix, int len, String versioned, ModuleDetails module, boolean withBody, ModuleVersionDetails version, String name, Node node) { super(offset, prefix, new FixedImageRetriever(MODULE), versioned, versioned.substring(len)); this.len = len; this.versioned = versioned; this.module = module; this.withBody = withBody; this.version = version; this.name = name; this.node = node; } @Override public String getDisplayString() { String str = super.getDisplayString(); /*if (withBody && EditorUtil.getPreferences() .getBoolean(LINKED_MODE)) { str = str.replaceAll("\".*\"", "\"<...>\""); }*/ return str; } @Override public Point getSelection(IDocument document) { final int off = offset+versioned.length()-prefix.length()-len; if (withBody) { final int verlen = version.getVersion().length(); return new Point(off-verlen-2, verlen); } else { return new Point(off, 0); } } @Override public void apply(IDocument document) { super.apply(document); if (withBody && //module.getVersions().size()>1 && //TODO: put this back in when sure it works getPreferences() .getBoolean(LINKED_MODE_ARGUMENTS)) { final LinkedModeModel linkedModeModel = new LinkedModeModel(); final Point selection = getSelection(document); List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>(); for (final ModuleVersionDetails d: module.getVersions()) { proposals.add(new ICompletionProposal() { @Override public Point getSelection(IDocument document) { return null; } @Override public Image getImage() { return CeylonResources.VERSION; } @Override public String getDisplayString() { return d.getVersion(); } @Override public IContextInformation getContextInformation() { return null; } @Override public String getAdditionalProposalInfo() { return "Repository: " + d.getOrigin(); } @Override public void apply(IDocument document) { try { document.replace( selection.x, selection.y, d.getVersion()); } catch (BadLocationException e) { e.printStackTrace(); } linkedModeModel.exit(ILinkedModeListener.UPDATE_CARET); } }); } ProposalPosition linkedPosition = new ProposalPosition(document, selection.x, selection.y, 0, proposals.toArray(NO_COMPLETIONS)); try { LinkedMode.addLinkedPosition( linkedModeModel, linkedPosition); CeylonEditor editor = (CeylonEditor) getCurrentEditor(); LinkedMode.installLinkedMode(editor, document, linkedModeModel, this, new LinkedMode.NullExitPolicy(), 1, selection.x+selection.y+2); } catch (BadLocationException ble) { ble.printStackTrace(); } } } @Override public String getAdditionalProposalInfo() { return getAdditionalProposalInfo(null); } @Override public String getAdditionalProposalInfo(IProgressMonitor monitor) { Scope scope = node.getScope(); Unit unit = node.getUnit(); return JDKUtils.isJDKModule(name) ? getDocumentationForModule(name, JDKUtils.jdk.version, "This module forms part of the Java SDK.", scope, unit) : getDocumentationFor(module, version.getVersion(), scope, unit); } @Override protected boolean qualifiedNameIsPath() { return true; } } public static final class JDKModuleProposal extends CompletionProposal { private final String name; public JDKModuleProposal(int offset, String prefix, int len, String versioned, String name) { super(offset, prefix, new FixedImageRetriever(MODULE), versioned, versioned.substring(len)); this.name = name; } @Override public String getAdditionalProposalInfo() { return getAdditionalProposalInfo(null); } @Override public String getAdditionalProposalInfo(IProgressMonitor monitor) { return getDocumentationForModule(name, JDKUtils.jdk.version, "This module forms part of the Java SDK.", null, null); } @Override protected boolean qualifiedNameIsPath() { return true; } } private static final SortedSet<String> JDK_MODULE_VERSION_SET = new TreeSet<String>(); { JDK_MODULE_VERSION_SET.add(JDKUtils.jdk.version); } @Deprecated static void addModuleCompletions( CeylonParseController controller, int offset, String prefix, Tree.ImportPath path, Node node, List<ICompletionProposal> result, boolean withBody, IProgressMonitor monitor) { String fullPath = fullPath(offset, prefix, path); addModuleCompletions(offset, prefix, node, result, fullPath.length(), fullPath+prefix, controller, withBody, monitor); } @Deprecated private static void addModuleCompletions( int offset, String prefix, Node node, List<ICompletionProposal> result, int len, String pfp, CeylonParseController controller, boolean withBody, IProgressMonitor monitor) { if (pfp.startsWith("java.")) { for (String name: new TreeSet<String> (JDKUtils.getJDKModuleNames())) { if (name.startsWith(pfp) && !moduleAlreadyImported(controller, name)) { result.add(new JDKModuleProposal(offset, prefix, len, getModuleString(withBody, name, JDKUtils.jdk.version), name)); } } } else { TypeChecker typeChecker = controller.getTypeChecker(); if (typeChecker!=null) { IProject project = controller.getProject(); Module mod = controller.getLastPhasedUnit() .getPackage() .getModule(); monitor.subTask("querying module repositories..."); ModuleQuery query = getModuleQuery(pfp, mod, project); query.setJvmBinaryMajor(Versions.JVM_BINARY_MAJOR_VERSION); query.setJvmBinaryMinor(Versions.JVM_BINARY_MINOR_VERSION); query.setJsBinaryMajor(Versions.JS_BINARY_MAJOR_VERSION); query.setJsBinaryMinor(Versions.JS_BINARY_MINOR_VERSION); final ModuleSearchResult results = typeChecker.getContext() .getRepositoryManager() .completeModules(query); monitor.subTask(null); // final ModuleSearchResult results = // getModuleSearchResults(pfp, typeChecker,project); if (results==null) return; for (ModuleDetails module: results.getResults()) { final String name = module.getName(); if (!name.equals(Module.DEFAULT_MODULE_NAME) && !moduleAlreadyImported(controller, name)) { if (getPreferences() .getBoolean(LINKED_MODE_ARGUMENTS)) { result.add(new ModuleProposal( offset, prefix, len, getModuleString( withBody, name, module.getLastVersion() .getVersion()), module, withBody, module.getLastVersion(), name, node)); } else { for (final ModuleVersionDetails version: module.getVersions().descendingSet()) { result.add(new ModuleProposal( offset, prefix, len, getModuleString( withBody, name, version.getVersion()), module, withBody, version, name, node)); } } } } } } } @Deprecated private static boolean moduleAlreadyImported( CeylonParseController cpc, String mod) { if (mod.equals(Module.LANGUAGE_MODULE_NAME)) { return true; } List<Tree.ModuleDescriptor> md = cpc.getParsedRootNode() .getModuleDescriptors(); if (!md.isEmpty()) { Tree.ImportModuleList iml = md.get(0).getImportModuleList(); if (iml!=null) { for (Tree.ImportModule im: iml.getImportModules()) { String path = getImportedName(im); if (path!=null && path.equals(mod)) { return true; } } } } //Disabled, because once the module is imported, it hangs around! // for (ModuleImport mi: node.getUnit().getPackage().getModule().getImports()) { // if (mi.getModule().getNameAsString().equals(mod)) { // return true; // } // } return false; } @Deprecated private static String getModuleString(boolean withBody, String name, String version) { if (!name.matches("^[a-z_]\\w*(\\.[a-z_]\\w*)*$")) { name = '"' + name + '"'; } return withBody ? name + " \"" + version + "\";" : name; } @Deprecated static void addModuleDescriptorCompletion( CeylonParseController cpc, int offset, String prefix, List<ICompletionProposal> result) { if (!"module".startsWith(prefix)) return; IFile file = cpc.getProject().getFile(cpc.getPath()); String moduleName = getPackageName(file); if (moduleName!=null) { result.add(new ModuleDescriptorProposal(offset, prefix, moduleName)); } } }